• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

voku / Stringy / 4439859016

pending completion
4439859016

Pull #40

github

GitHub
Merge 03132f915 into c453c88fb
Pull Request #40: Configure Renovate

1000 of 1024 relevant lines covered (97.66%)

69.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.02
/src/Stringy.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Stringy;
6

7
use Defuse\Crypto\Crypto;
8
use voku\helper\AntiXSS;
9
use voku\helper\ASCII;
10
use voku\helper\EmailCheck;
11
use voku\helper\URLify;
12
use voku\helper\UTF8;
13

14
/**
15
 * ## 🇷🇺 Русским гражданам
16
 * В Украине сейчас идет война. Силами РФ наносятся удары по гражданской инфраструктуре в [Харькове][1], [Киеве][2], [Чернигове][3], [Сумах][4], [Ирпене][5] и десятках других городов. Гибнут люди - и гражданское население, и военные, в том числе российские призывники, которых бросили воевать. Чтобы лишить собственный народ доступа к информации, правительство РФ запретило называть войну войной, закрыло независимые СМИ и принимает сейчас ряд диктаторских законов. Эти законы призваны заткнуть рот всем, кто против войны. За обычный призыв к миру сейчас можно получить несколько лет тюрьмы.
17
 * Не молчите! Молчание - знак вашего согласия с политикой российского правительства.
18
 *  **Вы можете сделать выбор НЕ МОЛЧАТЬ.**
19
 * ---
20
 * ## 🇺🇸 To people of Russia
21
 * There is a war in Ukraine right now. The forces of the Russian Federation are attacking civilian infrastructure in [Kharkiv][1], [Kyiv][2], [Chernihiv][3], [Sumy][4], [Irpin][5] and dozens of other cities. People are dying – both civilians and military servicemen, including Russian conscripts who were thrown into the fighting. In order to deprive its own people of access to information, the government of the Russian Federation has forbidden calling a war a war, shut down independent media and is passing a number of dictatorial laws. These laws are meant to silence all those who are against war. You can be jailed for multiple years for simply calling for peace.
22
 * Do not be silent! Silence is a sign that you accept the Russian government's policy.
23
 * **You can choose NOT TO BE SILENT.**
24
 * ---
25
 * - [1] https://cloudfront-us-east-2.images.arcpublishing.com/reuters/P7K2MSZDGFMIJPDD7CI2GIROJI.jpg "Kharkiv under attack"
26
 * - [2] https://gdb.voanews.com/01bd0000-0aff-0242-fad0-08d9fc92c5b3_cx0_cy5_cw0_w1023_r1_s.jpg "Kyiv under attack"
27
 * - [3] https://ichef.bbci.co.uk/news/976/cpsprodpb/163DD/production/_123510119_hi074310744.jpg "Chernihiv under attack"
28
 * - [4] https://www.youtube.com/watch?v=8K-bkqKKf2A "Sumy under attack"
29
 * - [5] https://cloudfront-us-east-2.images.arcpublishing.com/reuters/K4MTMLEHTRKGFK3GSKAT4GR3NE.jpg "Irpin under attack"
30
 *
31
 * @template-implements \IteratorAggregate<string>
32
 * @template-implements \ArrayAccess<array-key,string>
33
 */
34
class Stringy implements \ArrayAccess, \Countable, \IteratorAggregate, \JsonSerializable
35
{
36
    /**
37
     * An instance's string.
38
     *
39
     * @var string
40
     */
41
    protected $str;
42

43
    /**
44
     * The string's encoding, which should be one of the mbstring module's
45
     * supported encodings.
46
     *
47
     * @var string
48
     */
49
    protected $encoding;
50

51
    /**
52
     * @var UTF8
53
     */
54
    private $utf8;
55

56
    /**
57
     * @var ASCII
58
     */
59
    private $ascii;
60

61
    /**
62
     * Initializes a Stringy object and assigns both str and encoding properties
63
     * the supplied values. $str is cast to a string prior to assignment, and if
64
     * $encoding is not specified, it defaults to mb_internal_encoding(). Throws
65
     * an InvalidArgumentException if the first argument is an array or object
66
     * without a __toString method.
67
     *
68
     * @param object|scalar $str      [optional] <p>Value to modify, after being cast to string. Default: ''</p>
69
     * @param string        $encoding [optional] <p>The character encoding. Fallback: 'UTF-8'</p>
70
     *
71
     * @throws \InvalidArgumentException
72
     *                                   <p>if an array or object without a
73
     *                                   __toString method is passed as the first argument</p>
74
     *
75
     * @psalm-mutation-free
76
     */
77
    public function __construct($str = '', string $encoding = null)
78
    {
79
        /* @phpstan-ignore-next-line | always false in theory */
80
        if (\is_array($str)) {
3,643✔
81
            throw new \InvalidArgumentException(
3✔
82
                'Passed value cannot be an array'
3✔
83
            );
3✔
84
        }
85

86
        if (
87
            \is_object($str)
3,640✔
88
            &&
89
            !\method_exists($str, '__toString')
3,640✔
90
        ) {
91
            throw new \InvalidArgumentException(
4✔
92
                'Passed object must have a __toString method'
4✔
93
            );
4✔
94
        }
95

96
        $this->str = (string) $str;
3,637✔
97

98
        static $ASCII = null;
3,637✔
99
        if ($ASCII === null) {
3,637✔
100
            $ASCII = new ASCII();
×
101
        }
102
        $this->ascii = $ASCII;
3,637✔
103

104
        static $UTF8 = null;
3,637✔
105
        if ($UTF8 === null) {
3,637✔
106
            $UTF8 = new UTF8();
×
107
        }
108
        $this->utf8 = $UTF8;
3,637✔
109

110
        if ($encoding !== 'UTF-8') {
3,637✔
111
            $this->encoding = $this->utf8::normalize_encoding($encoding, 'UTF-8');
2,466✔
112
        } else {
113
            $this->encoding = $encoding;
2,705✔
114
        }
115
    }
116

117
    /**
118
     * Returns the value in $str.
119
     *
120
     * EXAMPLE: <code>
121
     * </code>
122
     *
123
     * @psalm-mutation-free
124
     *
125
     * @return string
126
     *                <p>The current value of the $str property.</p>
127
     */
128
    public function __toString()
129
    {
130
        return (string) $this->str;
1,145✔
131
    }
132

133
    /**
134
     * Return part of the string occurring after a specific string.
135
     *
136
     * EXAMPLE: <code>
137
     * s('宮本 茂')->after('本'); // ' 茂'
138
     * </code>
139
     *
140
     * @param string $string <p>The delimiting string.</p>
141
     *
142
     * @psalm-mutation-free
143
     *
144
     * @return static
145
     */
146
    public function after(string $string): self
147
    {
148
        $strArray = UTF8::str_split_pattern(
4✔
149
            $this->str,
4✔
150
            $string
4✔
151
        );
4✔
152

153
        unset($strArray[0]);
4✔
154

155
        return new static(
4✔
156
            \implode(' ', $strArray),
4✔
157
            $this->encoding
4✔
158
        );
4✔
159
    }
160

161
    /**
162
     * Gets the substring after the first occurrence of a separator.
163
     * If no match is found returns new empty Stringy object.
164
     *
165
     * EXAMPLE: <code>
166
     * s('</b></b>')->afterFirst('b'); // '></b>'
167
     * </code>
168
     *
169
     * @param string $separator
170
     *
171
     * @psalm-mutation-free
172
     *
173
     * @return static
174
     */
175
    public function afterFirst(string $separator): self
176
    {
177
        return static::create(
3✔
178
            $this->utf8::str_substr_after_first_separator(
3✔
179
                $this->str,
3✔
180
                $separator,
3✔
181
                $this->encoding
3✔
182
            )
3✔
183
        );
3✔
184
    }
185

186
    /**
187
     * Gets the substring after the first occurrence of a separator.
188
     * If no match is found returns new empty Stringy object.
189
     *
190
     * EXAMPLE: <code>
191
     * s('</B></B>')->afterFirstIgnoreCase('b'); // '></B>'
192
     * </code>
193
     *
194
     * @param string $separator
195
     *
196
     * @psalm-mutation-free
197
     *
198
     * @return static
199
     */
200
    public function afterFirstIgnoreCase(string $separator): self
201
    {
202
        return static::create(
2✔
203
            $this->utf8::str_isubstr_after_first_separator(
2✔
204
                $this->str,
2✔
205
                $separator,
2✔
206
                $this->encoding
2✔
207
            )
2✔
208
        );
2✔
209
    }
210

211
    /**
212
     * Gets the substring after the last occurrence of a separator.
213
     * If no match is found returns new empty Stringy object.
214
     *
215
     * EXAMPLE: <code>
216
     * s('</b></b>')->afterLast('b'); // '>'
217
     * </code>
218
     *
219
     * @param string $separator
220
     *
221
     * @psalm-mutation-free
222
     *
223
     * @return static
224
     */
225
    public function afterLast(string $separator): self
226
    {
227
        return static::create(
2✔
228
            $this->utf8::str_substr_after_last_separator(
2✔
229
                $this->str,
2✔
230
                $separator,
2✔
231
                $this->encoding
2✔
232
            )
2✔
233
        );
2✔
234
    }
235

236
    /**
237
     * Gets the substring after the last occurrence of a separator.
238
     * If no match is found returns new empty Stringy object.
239
     *
240
     * EXAMPLE: <code>
241
     * s('</B></B>')->afterLastIgnoreCase('b'); // '>'
242
     * </code>
243
     *
244
     * @param string $separator
245
     *
246
     * @psalm-mutation-free
247
     *
248
     * @return static
249
     */
250
    public function afterLastIgnoreCase(string $separator): self
251
    {
252
        return static::create(
2✔
253
            $this->utf8::str_isubstr_after_last_separator(
2✔
254
                $this->str,
2✔
255
                $separator,
2✔
256
                $this->encoding
2✔
257
            )
2✔
258
        );
2✔
259
    }
260

261
    /**
262
     * Returns a new string with $suffix appended.
263
     *
264
     * EXAMPLE: <code>
265
     * s('fòô')->append('bàř'); // 'fòôbàř'
266
     * </code>
267
     *
268
     * @param string ...$suffix <p>The string to append.</p>
269
     *
270
     * @psalm-mutation-free
271
     *
272
     * @return static
273
     *                <p>Object with appended $suffix.</p>
274
     */
275
    public function append(string ...$suffix): self
276
    {
277
        if (\count($suffix) <= 1) {
15✔
278
            $suffix = $suffix[0];
13✔
279
        } else {
280
            $suffix = \implode('', $suffix);
2✔
281
        }
282

283
        return static::create($this->str . $suffix, $this->encoding);
15✔
284
    }
285

286
    /**
287
     * Append an password (limited to chars that are good readable).
288
     *
289
     * EXAMPLE: <code>
290
     * s('')->appendPassword(8); // e.g.: '89bcdfgh'
291
     * </code>
292
     *
293
     * @param int $length <p>Length of the random string.</p>
294
     *
295
     * @return static
296
     *                <p>Object with appended password.</p>
297
     */
298
    public function appendPassword(int $length): self
299
    {
300
        return $this->appendRandomString(
2✔
301
            $length,
2✔
302
            '2346789bcdfghjkmnpqrtvwxyzBCDFGHJKLMNPQRTVWXYZ!?_#'
2✔
303
        );
2✔
304
    }
305

306
    /**
307
     * Append an random string.
308
     *
309
     * EXAMPLE: <code>
310
     * s('')->appendUniqueIdentifier(5, 'ABCDEFGHI'); // e.g.: 'CDEHI'
311
     * </code>
312
     *
313
     * @param int    $length        <p>Length of the random string.</p>
314
     * @param string $possibleChars [optional] <p>Characters string for the random selection.</p>
315
     *
316
     * @return static
317
     *                <p>Object with appended random string.</p>
318
     */
319
    public function appendRandomString(int $length, string $possibleChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'): self
320
    {
321
        $str = $this->utf8::get_random_string($length, $possibleChars);
4✔
322

323
        return $this->append($str);
4✔
324
    }
325

326
    /**
327
     * Returns a new string with $suffix appended.
328
     *
329
     * EXAMPLE: <code>
330
     * </code>
331
     *
332
     * @param CollectionStringy|static ...$suffix <p>The Stringy objects to append.</p>
333
     *
334
     * @phpstan-param CollectionStringy<int,static>|static ...$suffix
335
     *
336
     * @psalm-mutation-free
337
     *
338
     * @return static
339
     *                <p>Object with appended $suffix.</p>
340
     */
341
    public function appendStringy(...$suffix): self
342
    {
343
        $suffixStr = '';
7✔
344
        foreach ($suffix as $suffixTmp) {
7✔
345
            if ($suffixTmp instanceof CollectionStringy) {
7✔
346
                $suffixStr .= $suffixTmp->implode('');
1✔
347
            } else {
348
                $suffixStr .= $suffixTmp->toString();
7✔
349
            }
350
        }
351

352
        return static::create($this->str . $suffixStr, $this->encoding);
7✔
353
    }
354

355
    /**
356
     * Append an unique identifier.
357
     *
358
     * EXAMPLE: <code>
359
     * s('')->appendUniqueIdentifier(); // e.g.: '1f3870be274f6c49b3e31a0c6728957f'
360
     * </code>
361
     *
362
     * @param int|string $entropyExtra [optional] <p>Extra entropy via a string or int value.</p>
363
     * @param bool       $md5          [optional] <p>Return the unique identifier as md5-hash? Default: true</p>
364
     *
365
     * @return static
366
     *                <p>Object with appended unique identifier as md5-hash.</p>
367
     */
368
    public function appendUniqueIdentifier($entropyExtra = '', bool $md5 = true): self
369
    {
370
        return $this->append(
2✔
371
            $this->utf8::get_unique_string($entropyExtra, $md5)
2✔
372
        );
2✔
373
    }
374

375
    /**
376
     * Returns the character at $index, with indexes starting at 0.
377
     *
378
     * EXAMPLE: <code>
379
     * s('fòôbàř')->at(3); // 'b'
380
     * </code>
381
     *
382
     * @param int $index <p>Position of the character.</p>
383
     *
384
     * @psalm-mutation-free
385
     *
386
     * @return static
387
     *                <p>The character at $index.</p>
388
     */
389
    public function at(int $index): self
390
    {
391
        return static::create($this->utf8::char_at($this->str, $index), $this->encoding);
24✔
392
    }
393

394
    /**
395
     * Decode the base64 encoded string.
396
     *
397
     * EXAMPLE: <code>
398
     * </code>
399
     *
400
     * @psalm-mutation-free
401
     *
402
     * @return self
403
     */
404
    public function base64Decode(): self
405
    {
406
        return static::create(
2✔
407
            \base64_decode($this->str, true),
2✔
408
            $this->encoding
2✔
409
        );
2✔
410
    }
411

412
    /**
413
     * Encode the string to base64.
414
     *
415
     * EXAMPLE: <code>
416
     * </code>
417
     *
418
     * @psalm-mutation-free
419
     *
420
     * @return self
421
     */
422
    public function base64Encode(): self
423
    {
424
        return static::create(
2✔
425
            \base64_encode($this->str),
2✔
426
            $this->encoding
2✔
427
        );
2✔
428
    }
429

430
    /**
431
     * Creates a hash from the string using the CRYPT_BLOWFISH algorithm.
432
     *
433
     * WARNING: Using this algorithm, will result in the ```$this->str```
434
     *          being truncated to a maximum length of 72 characters.
435
     *
436
     * EXAMPLE: <code>
437
     * </code>
438
     *
439
     * @param array<array-key, int|string> $options [optional] <p>An array of bcrypt hasing options.</p>
440
     *
441
     * @psalm-mutation-free
442
     *
443
     * @return static
444
     */
445
    public function bcrypt(array $options = []): self
446
    {
447
        return new static(
3✔
448
            \password_hash(
3✔
449
                $this->str,
3✔
450
                \PASSWORD_BCRYPT,
3✔
451
                $options
3✔
452
            ),
3✔
453
            $this->encoding
3✔
454
        );
3✔
455
    }
456

457
    /**
458
     * Return part of the string occurring before a specific string.
459
     *
460
     * EXAMPLE: <code>
461
     * </code>
462
     *
463
     * @param string $string <p>The delimiting string.</p>
464
     *
465
     * @psalm-mutation-free
466
     *
467
     * @return static
468
     */
469
    public function before(string $string): self
470
    {
471
        $strArray = UTF8::str_split_pattern(
4✔
472
            $this->str,
4✔
473
            $string,
4✔
474
            1
4✔
475
        );
4✔
476

477
        return new static(
4✔
478
            $strArray[0] ?? '',
4✔
479
            $this->encoding
4✔
480
        );
4✔
481
    }
482

483
    /**
484
     * Gets the substring before the first occurrence of a separator.
485
     * If no match is found returns new empty Stringy object.
486
     *
487
     * EXAMPLE: <code>
488
     * s('</b></b>')->beforeFirst('b'); // '</'
489
     * </code>
490
     *
491
     * @param string $separator
492
     *
493
     * @psalm-mutation-free
494
     *
495
     * @return static
496
     */
497
    public function beforeFirst(string $separator): self
498
    {
499
        return static::create(
2✔
500
            $this->utf8::str_substr_before_first_separator(
2✔
501
                $this->str,
2✔
502
                $separator,
2✔
503
                $this->encoding
2✔
504
            )
2✔
505
        );
2✔
506
    }
507

508
    /**
509
     * Gets the substring before the first occurrence of a separator.
510
     * If no match is found returns new empty Stringy object.
511
     *
512
     * EXAMPLE: <code>
513
     * s('</B></B>')->beforeFirstIgnoreCase('b'); // '</'
514
     * </code>
515
     *
516
     * @param string $separator
517
     *
518
     * @psalm-mutation-free
519
     *
520
     * @return static
521
     */
522
    public function beforeFirstIgnoreCase(string $separator): self
523
    {
524
        return static::create(
2✔
525
            $this->utf8::str_isubstr_before_first_separator(
2✔
526
                $this->str,
2✔
527
                $separator,
2✔
528
                $this->encoding
2✔
529
            )
2✔
530
        );
2✔
531
    }
532

533
    /**
534
     * Gets the substring before the last occurrence of a separator.
535
     * If no match is found returns new empty Stringy object.
536
     *
537
     * EXAMPLE: <code>
538
     * s('</b></b>')->beforeLast('b'); // '</b></'
539
     * </code>
540
     *
541
     * @param string $separator
542
     *
543
     * @psalm-mutation-free
544
     *
545
     * @return static
546
     */
547
    public function beforeLast(string $separator): self
548
    {
549
        return static::create(
2✔
550
            $this->utf8::str_substr_before_last_separator(
2✔
551
                $this->str,
2✔
552
                $separator,
2✔
553
                $this->encoding
2✔
554
            )
2✔
555
        );
2✔
556
    }
557

558
    /**
559
     * Gets the substring before the last occurrence of a separator.
560
     * If no match is found returns new empty Stringy object.
561
     *
562
     * EXAMPLE: <code>
563
     * s('</B></B>')->beforeLastIgnoreCase('b'); // '</B></'
564
     * </code>
565
     *
566
     * @param string $separator
567
     *
568
     * @psalm-mutation-free
569
     *
570
     * @return static
571
     */
572
    public function beforeLastIgnoreCase(string $separator): self
573
    {
574
        return static::create(
2✔
575
            $this->utf8::str_isubstr_before_last_separator(
2✔
576
                $this->str,
2✔
577
                $separator,
2✔
578
                $this->encoding
2✔
579
            )
2✔
580
        );
2✔
581
    }
582

583
    /**
584
     * Returns the substring between $start and $end, if found, or an empty
585
     * string. An optional offset may be supplied from which to begin the
586
     * search for the start string.
587
     *
588
     * EXAMPLE: <code>
589
     * s('{foo} and {bar}')->between('{', '}'); // 'foo'
590
     * </code>
591
     *
592
     * @param string $start  <p>Delimiter marking the start of the substring.</p>
593
     * @param string $end    <p>Delimiter marking the end of the substring.</p>
594
     * @param int    $offset [optional] <p>Index from which to begin the search. Default: 0</p>
595
     *
596
     * @psalm-mutation-free
597
     *
598
     * @return static
599
     *                <p>Object whose $str is a substring between $start and $end.</p>
600
     */
601
    public function between(string $start, string $end, int $offset = null): self
602
    {
603
        $str = $this->utf8::between(
48✔
604
            $this->str,
48✔
605
            $start,
48✔
606
            $end,
48✔
607
            (int) $offset,
48✔
608
            $this->encoding
48✔
609
        );
48✔
610

611
        return static::create($str, $this->encoding);
48✔
612
    }
613

614
    /**
615
     * Call a user function.
616
     *
617
     * EXAMPLE: <code>
618
     * S::create('foo bar lall')->callUserFunction(static function ($str) {
619
     *     return UTF8::str_limit($str, 8);
620
     * })->toString(); // "foo bar…"
621
     * </code>
622
     *
623
     * @param callable $function
624
     * @param mixed    ...$parameter
625
     *
626
     * @psalm-mutation-free
627
     *
628
     * @return static
629
     *                <p>Object having a $str changed via $function.</p>
630
     */
631
    public function callUserFunction(callable $function, ...$parameter): self
632
    {
633
        $str = $function($this->str, ...$parameter);
2✔
634

635
        return static::create(
2✔
636
            $str,
2✔
637
            $this->encoding
2✔
638
        );
2✔
639
    }
640

641
    /**
642
     * Returns a camelCase version of the string. Trims surrounding spaces,
643
     * capitalizes letters following digits, spaces, dashes and underscores,
644
     * and removes spaces, dashes, as well as underscores.
645
     *
646
     * EXAMPLE: <code>
647
     * s('Camel-Case')->camelize(); // 'camelCase'
648
     * </code>
649
     *
650
     * @psalm-mutation-free
651
     *
652
     * @return static
653
     *                <p>Object with $str in camelCase.</p>
654
     */
655
    public function camelize(): self
656
    {
657
        return static::create(
57✔
658
            $this->utf8::str_camelize($this->str, $this->encoding),
57✔
659
            $this->encoding
57✔
660
        );
57✔
661
    }
662

663
    /**
664
     * Returns the string with the first letter of each word capitalized,
665
     * except for when the word is a name which shouldn't be capitalized.
666
     *
667
     * EXAMPLE: <code>
668
     * s('jaap de hoop scheffer')->capitalizePersonName(); // 'Jaap de Hoop Scheffer'
669
     * </code>
670
     *
671
     * @psalm-mutation-free
672
     *
673
     * @return static
674
     *                <p>Object with $str capitalized.</p>
675
     */
676
    public function capitalizePersonalName(): self
677
    {
678
        return static::create(
78✔
679
            $this->utf8::str_capitalize_name($this->str),
78✔
680
            $this->encoding
78✔
681
        );
78✔
682
    }
683

684
    /**
685
     * Returns an array consisting of the characters in the string.
686
     *
687
     * EXAMPLE: <code>
688
     * s('fòôbàř')->chars(); // ['f', 'ò', 'ô', 'b', 'à', 'ř']
689
     * </code>
690
     *
691
     * @psalm-mutation-free
692
     *
693
     * @return string[]
694
     *                  <p>An array of string chars.</p>
695
     */
696
    public function chars(): array
697
    {
698
        /** @var string[] */
699
        return $this->utf8::str_split($this->str);
14✔
700
    }
701

702
    /**
703
     * Splits the string into chunks of Stringy objects.
704
     *
705
     * EXAMPLE: <code>
706
     * s('foobar')->chunk(3); // ['foo', 'bar']
707
     * </code>
708
     *
709
     * @param int $length [optional] <p>Max character length of each array element.</p>
710
     *
711
     * @psalm-mutation-free
712
     *
713
     * @return static[]
714
     *                  <p>An array of Stringy objects.</p>
715
     *
716
     * @phpstan-return array<int,static>
717
     */
718
    public function chunk(int $length = 1): array
719
    {
720
        if ($length < 1) {
13✔
721
            throw new \InvalidArgumentException('The chunk length must be greater than zero.');
×
722
        }
723

724
        if ($this->str === '') {
13✔
725
            return [];
2✔
726
        }
727

728
        $chunks = $this->utf8::str_split($this->str, $length);
11✔
729

730
        foreach ($chunks as &$value) {
11✔
731
            $value = static::create($value, $this->encoding);
11✔
732
        }
733

734
        /** @noinspection PhpSillyAssignmentInspection */
735
        /** @var static[] $chunks */
736
        $chunks = $chunks;
11✔
737

738
        return $chunks;
11✔
739
    }
740

741
    /**
742
     * Splits the string into chunks of Stringy objects collection.
743
     *
744
     * EXAMPLE: <code>
745
     * </code>
746
     *
747
     * @param int $length [optional] <p>Max character length of each array element.</p>
748
     *
749
     * @psalm-mutation-free
750
     *
751
     * @return CollectionStringy|static[]
752
     *                                    <p>An collection of Stringy objects.</p>
753
     *
754
     * @phpstan-return CollectionStringy<int,static>
755
     */
756
    public function chunkCollection(int $length = 1): CollectionStringy
757
    {
758
        /**
759
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to the collection class
760
         */
761
        return CollectionStringy::create(
7✔
762
            $this->chunk($length)
7✔
763
        );
7✔
764
    }
765

766
    /**
767
     * Trims the string and replaces consecutive whitespace characters with a
768
     * single space. This includes tabs and newline characters, as well as
769
     * multibyte whitespace such as the thin space and ideographic space.
770
     *
771
     * EXAMPLE: <code>
772
     * s('   Ο     συγγραφέας  ')->collapseWhitespace(); // 'Ο συγγραφέας'
773
     * </code>
774
     *
775
     * @psalm-mutation-free
776
     *
777
     * @return static
778
     *                <p>Object with a trimmed $str and condensed whitespace.</p>
779
     */
780
    public function collapseWhitespace(): self
781
    {
782
        return static::create(
39✔
783
            $this->utf8::collapse_whitespace($this->str),
39✔
784
            $this->encoding
39✔
785
        );
39✔
786
    }
787

788
    /**
789
     * Returns true if the string contains $needle, false otherwise. By default
790
     * the comparison is case-sensitive, but can be made insensitive by setting
791
     * $caseSensitive to false.
792
     *
793
     * EXAMPLE: <code>
794
     * s('Ο συγγραφέας είπε')->contains('συγγραφέας'); // true
795
     * </code>
796
     *
797
     * @param string $needle        <p>Substring to look for.</p>
798
     * @param bool   $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
799
     *
800
     * @psalm-mutation-free
801
     *
802
     * @return bool
803
     *              <p>Whether or not $str contains $needle.</p>
804
     */
805
    public function contains(string $needle, bool $caseSensitive = true): bool
806
    {
807
        return $this->utf8::str_contains(
63✔
808
            $this->str,
63✔
809
            $needle,
63✔
810
            $caseSensitive
63✔
811
        );
63✔
812
    }
813

814
    /**
815
     * Returns true if the string contains all $needles, false otherwise. By
816
     * default the comparison is case-sensitive, but can be made insensitive by
817
     * setting $caseSensitive to false.
818
     *
819
     * EXAMPLE: <code>
820
     * s('foo & bar')->containsAll(['foo', 'bar']); // true
821
     * </code>
822
     *
823
     * @param string[] $needles       <p>SubStrings to look for.</p>
824
     * @param bool     $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
825
     *
826
     * @psalm-mutation-free
827
     *
828
     * @return bool
829
     *              <p>Whether or not $str contains $needle.</p>
830
     */
831
    public function containsAll(array $needles, bool $caseSensitive = true): bool
832
    {
833
        return $this->utf8::str_contains_all(
131✔
834
            $this->str,
131✔
835
            $needles,
131✔
836
            $caseSensitive
131✔
837
        );
131✔
838
    }
839

840
    /**
841
     * Returns true if the string contains any $needles, false otherwise. By
842
     * default the comparison is case-sensitive, but can be made insensitive by
843
     * setting $caseSensitive to false.
844
     *
845
     * EXAMPLE: <code>
846
     * s('str contains foo')->containsAny(['foo', 'bar']); // true
847
     * </code>
848
     *
849
     * @param string[] $needles       <p>SubStrings to look for.</p>
850
     * @param bool     $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
851
     *
852
     * @psalm-mutation-free
853
     *
854
     * @return bool
855
     *              <p>Whether or not $str contains $needle.</p>
856
     */
857
    public function containsAny(array $needles, bool $caseSensitive = true): bool
858
    {
859
        return $this->utf8::str_contains_any(
129✔
860
            $this->str,
129✔
861
            $needles,
129✔
862
            $caseSensitive
129✔
863
        );
129✔
864
    }
865

866
    /**
867
     * Checks if string starts with "BOM" (Byte Order Mark Character) character.
868
     *
869
     * EXAMPLE: <code>s("\xef\xbb\xbf foobar")->containsBom(); // true</code>
870
     *
871
     * @psalm-mutation-free
872
     *
873
     * @return bool
874
     *              <strong>true</strong> if the string has BOM at the start,<br>
875
     *              <strong>false</strong> otherwise
876
     */
877
    public function containsBom(): bool
878
    {
879
        return $this->utf8::string_has_bom($this->str);
×
880
    }
881

882
    /**
883
     * Returns the length of the string, implementing the countable interface.
884
     *
885
     * EXAMPLE: <code>
886
     * </code>
887
     *
888
     * @psalm-mutation-free
889
     *
890
     * @return int
891
     *             <p>The number of characters in the string, given the encoding.</p>
892
     */
893
    public function count(): int
894
    {
895
        return $this->length();
3✔
896
    }
897

898
    /**
899
     * Returns the number of occurrences of $substring in the given string.
900
     * By default, the comparison is case-sensitive, but can be made insensitive
901
     * by setting $caseSensitive to false.
902
     *
903
     * EXAMPLE: <code>
904
     * s('Ο συγγραφέας είπε')->countSubstr('α'); // 2
905
     * </code>
906
     *
907
     * @param string $substring     <p>The substring to search for.</p>
908
     * @param bool   $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
909
     *
910
     * @psalm-mutation-free
911
     *
912
     * @return int
913
     */
914
    public function countSubstr(string $substring, bool $caseSensitive = true): int
915
    {
916
        return $this->utf8::substr_count_simple(
45✔
917
            $this->str,
45✔
918
            $substring,
45✔
919
            $caseSensitive,
45✔
920
            $this->encoding
45✔
921
        );
45✔
922
    }
923

924
    /**
925
     * Calculates the crc32 polynomial of a string.
926
     *
927
     * EXAMPLE: <code>
928
     * </code>
929
     *
930
     * @psalm-mutation-free
931
     *
932
     * @return int
933
     */
934
    public function crc32(): int
935
    {
936
        return \crc32($this->str);
2✔
937
    }
938

939
    /**
940
     * Creates a Stringy object and assigns both str and encoding properties
941
     * the supplied values. $str is cast to a string prior to assignment, and if
942
     * $encoding is not specified, it defaults to mb_internal_encoding(). It
943
     * then returns the initialized object. Throws an InvalidArgumentException
944
     * if the first argument is an array or object without a __toString method.
945
     *
946
     * @param mixed  $str      [optional] <p>Value to modify, after being cast to string. Default: ''</p>
947
     * @param string $encoding [optional] <p>The character encoding. Fallback: 'UTF-8'</p>
948
     *
949
     * @throws \InvalidArgumentException
950
     *                                   <p>if an array or object without a
951
     *                                   __toString method is passed as the first argument</p>
952
     *
953
     * @return static
954
     *                <p>A Stringy object.</p>
955
     * @phpstan-pure
956
     */
957
    public static function create($str = '', string $encoding = null): self
958
    {
959
        return new static($str, $encoding);
3,571✔
960
    }
961

962
    /**
963
     * One-way string encryption (hashing).
964
     *
965
     * Hash the string using the standard Unix DES-based algorithm or an
966
     * alternative algorithm that may be available on the system.
967
     *
968
     * PS: if you need encrypt / decrypt, please use ```static::encrypt($password)```
969
     *     and ```static::decrypt($password)```
970
     *
971
     * EXAMPLE: <code>
972
     * </code>
973
     *
974
     * @param string $salt <p>A salt string to base the hashing on.</p>
975
     *
976
     * @psalm-mutation-free
977
     *
978
     * @return static
979
     */
980
    public function crypt(string $salt): self
981
    {
982
        return new static(
3✔
983
            \crypt(
3✔
984
                $this->str,
3✔
985
                $salt
3✔
986
            ),
3✔
987
            $this->encoding
3✔
988
        );
3✔
989
    }
990

991
    /**
992
     * Returns a lowercase and trimmed string separated by dashes. Dashes are
993
     * inserted before uppercase characters (with the exception of the first
994
     * character of the string), and in place of spaces as well as underscores.
995
     *
996
     * EXAMPLE: <code>
997
     * s('fooBar')->dasherize(); // 'foo-bar'
998
     * </code>
999
     *
1000
     * @psalm-mutation-free
1001
     *
1002
     * @return static
1003
     *                <p>Object with a dasherized $str</p>
1004
     */
1005
    public function dasherize(): self
1006
    {
1007
        return static::create(
57✔
1008
            $this->utf8::str_dasherize($this->str),
57✔
1009
            $this->encoding
57✔
1010
        );
57✔
1011
    }
1012

1013
    /**
1014
     * Decrypt the string.
1015
     *
1016
     * EXAMPLE: <code>
1017
     * </code>
1018
     *
1019
     * @param string $password The key for decrypting
1020
     *
1021
     * @psalm-mutation-free
1022
     *
1023
     * @return static
1024
     */
1025
    public function decrypt(string $password): self
1026
    {
1027
        /**
1028
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to vendor stuff
1029
         */
1030
        return new static(
5✔
1031
            Crypto::decryptWithPassword($this->str, $password),
5✔
1032
            $this->encoding
5✔
1033
        );
5✔
1034
    }
1035

1036
    /**
1037
     * Returns a lowercase and trimmed string separated by the given delimiter.
1038
     * Delimiters are inserted before uppercase characters (with the exception
1039
     * of the first character of the string), and in place of spaces, dashes,
1040
     * and underscores. Alpha delimiters are not converted to lowercase.
1041
     *
1042
     * EXAMPLE: <code>
1043
     * s('fooBar')->delimit('::'); // 'foo::bar'
1044
     * </code>
1045
     *
1046
     * @param string $delimiter <p>Sequence used to separate parts of the string.</p>
1047
     *
1048
     * @psalm-mutation-free
1049
     *
1050
     * @return static
1051
     *                <p>Object with a delimited $str.</p>
1052
     */
1053
    public function delimit(string $delimiter): self
1054
    {
1055
        return static::create(
90✔
1056
            $this->utf8::str_delimit($this->str, $delimiter),
90✔
1057
            $this->encoding
90✔
1058
        );
90✔
1059
    }
1060

1061
    /**
1062
     * Encode the given string into the given $encoding + set the internal character encoding.
1063
     *
1064
     * EXAMPLE: <code>
1065
     * </code>
1066
     *
1067
     * @param string $new_encoding         <p>The desired character encoding.</p>
1068
     * @param bool   $auto_detect_encoding [optional] <p>Auto-detect the current string-encoding</p>
1069
     *
1070
     * @psalm-mutation-free
1071
     *
1072
     * @return static
1073
     */
1074
    public function encode(string $new_encoding, bool $auto_detect_encoding = false): self
1075
    {
1076
        if ($auto_detect_encoding) {
2✔
1077
            $str = $this->utf8::encode(
1✔
1078
                $new_encoding,
1✔
1079
                $this->str
1✔
1080
            );
1✔
1081
        } else {
1082
            $str = $this->utf8::encode(
1✔
1083
                $new_encoding,
1✔
1084
                $this->str,
1✔
1085
                false,
1✔
1086
                $this->encoding
1✔
1087
            );
1✔
1088
        }
1089

1090
        return new static($str, $new_encoding);
2✔
1091
    }
1092

1093
    /**
1094
     * Encrypt the string.
1095
     *
1096
     * EXAMPLE: <code>
1097
     * </code>
1098
     *
1099
     * @param string $password <p>The key for encrypting</p>
1100
     *
1101
     * @psalm-mutation-free
1102
     *
1103
     * @return static
1104
     */
1105
    public function encrypt(string $password): self
1106
    {
1107
        /**
1108
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to vendor stuff
1109
         */
1110
        return new static(
4✔
1111
            Crypto::encryptWithPassword($this->str, $password),
4✔
1112
            $this->encoding
4✔
1113
        );
4✔
1114
    }
1115

1116
    /**
1117
     * Returns true if the string ends with $substring, false otherwise. By
1118
     * default, the comparison is case-sensitive, but can be made insensitive
1119
     * by setting $caseSensitive to false.
1120
     *
1121
     * EXAMPLE: <code>
1122
     * s('fòôbàř')->endsWith('bàř', true); // true
1123
     * </code>
1124
     *
1125
     * @param string $substring     <p>The substring to look for.</p>
1126
     * @param bool   $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
1127
     *
1128
     * @psalm-mutation-free
1129
     *
1130
     * @return bool
1131
     *              <p>Whether or not $str ends with $substring.</p>
1132
     */
1133
    public function endsWith(string $substring, bool $caseSensitive = true): bool
1134
    {
1135
        if ($caseSensitive) {
97✔
1136
            return $this->utf8::str_ends_with($this->str, $substring);
53✔
1137
        }
1138

1139
        return $this->utf8::str_iends_with($this->str, $substring);
44✔
1140
    }
1141

1142
    /**
1143
     * Returns true if the string ends with any of $substrings, false otherwise.
1144
     * By default, the comparison is case-sensitive, but can be made insensitive
1145
     * by setting $caseSensitive to false.
1146
     *
1147
     * EXAMPLE: <code>
1148
     * s('fòôbàř')->endsWithAny(['bàř', 'baz'], true); // true
1149
     * </code>
1150
     *
1151
     * @param string[] $substrings    <p>Substrings to look for.</p>
1152
     * @param bool     $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
1153
     *
1154
     * @psalm-mutation-free
1155
     *
1156
     * @return bool
1157
     *              <p>Whether or not $str ends with $substring.</p>
1158
     */
1159
    public function endsWithAny(array $substrings, bool $caseSensitive = true): bool
1160
    {
1161
        if ($caseSensitive) {
33✔
1162
            return $this->utf8::str_ends_with_any($this->str, $substrings);
21✔
1163
        }
1164

1165
        return $this->utf8::str_iends_with_any($this->str, $substrings);
12✔
1166
    }
1167

1168
    /**
1169
     * Ensures that the string begins with $substring. If it doesn't, it's
1170
     * prepended.
1171
     *
1172
     * EXAMPLE: <code>
1173
     * s('foobar')->ensureLeft('http://'); // 'http://foobar'
1174
     * </code>
1175
     *
1176
     * @param string $substring <p>The substring to add if not present.</p>
1177
     *
1178
     * @psalm-mutation-free
1179
     *
1180
     * @return static
1181
     *                <p>Object with its $str prefixed by the $substring.</p>
1182
     */
1183
    public function ensureLeft(string $substring): self
1184
    {
1185
        return static::create(
30✔
1186
            $this->utf8::str_ensure_left($this->str, $substring),
30✔
1187
            $this->encoding
30✔
1188
        );
30✔
1189
    }
1190

1191
    /**
1192
     * Ensures that the string ends with $substring. If it doesn't, it's appended.
1193
     *
1194
     * EXAMPLE: <code>
1195
     * s('foobar')->ensureRight('.com'); // 'foobar.com'
1196
     * </code>
1197
     *
1198
     * @param string $substring <p>The substring to add if not present.</p>
1199
     *
1200
     * @psalm-mutation-free
1201
     *
1202
     * @return static
1203
     *                <p>Object with its $str suffixed by the $substring.</p>
1204
     */
1205
    public function ensureRight(string $substring): self
1206
    {
1207
        return static::create(
30✔
1208
            $this->utf8::str_ensure_right($this->str, $substring),
30✔
1209
            $this->encoding
30✔
1210
        );
30✔
1211
    }
1212

1213
    /**
1214
     * Create a escape html version of the string via "htmlspecialchars()".
1215
     *
1216
     * EXAMPLE: <code>
1217
     * s('<∂∆ onerror="alert(xss)">')->escape(); // '&lt;∂∆ onerror=&quot;alert(xss)&quot;&gt;'
1218
     * </code>
1219
     *
1220
     * @psalm-mutation-free
1221
     *
1222
     * @return static
1223
     */
1224
    public function escape(): self
1225
    {
1226
        return static::create(
12✔
1227
            $this->utf8::htmlspecialchars(
12✔
1228
                $this->str,
12✔
1229
                \ENT_QUOTES | \ENT_SUBSTITUTE,
12✔
1230
                $this->encoding
12✔
1231
            ),
12✔
1232
            $this->encoding
12✔
1233
        );
12✔
1234
    }
1235

1236
    /**
1237
     * Split a string by a string.
1238
     *
1239
     * EXAMPLE: <code>
1240
     * </code>
1241
     *
1242
     * @param string $delimiter <p>The boundary string</p>
1243
     * @param int    $limit     [optional] <p>The maximum number of elements in the exploded
1244
     *                          collection.</p>
1245
     *
1246
     *   - If limit is set and positive, the returned collection will contain a maximum of limit elements with the last
1247
     *   element containing the rest of string.
1248
     *   - If the limit parameter is negative, all components except the last -limit are returned.
1249
     *   - If the limit parameter is zero, then this is treated as 1
1250
     *
1251
     * @psalm-mutation-free
1252
     *
1253
     * @return array<int,static>
1254
     */
1255
    public function explode(string $delimiter, int $limit = \PHP_INT_MAX): array
1256
    {
1257
        if ($this->str === '') {
3✔
1258
            return [];
×
1259
        }
1260

1261
        /** @phpstan-ignore-next-line - FP -> non-empty-string is already checked */
1262
        $strings = \explode($delimiter, $this->str, $limit);
3✔
1263
        /** @phpstan-ignore-next-line - if "$delimiter" is an empty string, then "explode()" will return "false" */
1264
        if ($strings === false) {
3✔
1265
            $strings = [];
×
1266
        }
1267

1268
        return \array_map(
3✔
1269
            function ($str) {
3✔
1270
                return new static($str, $this->encoding);
3✔
1271
            },
3✔
1272
            $strings
3✔
1273
        );
3✔
1274
    }
1275

1276
    /**
1277
     * Split a string by a string.
1278
     *
1279
     * EXAMPLE: <code>
1280
     * </code>
1281
     *
1282
     * @param string $delimiter <p>The boundary string</p>
1283
     * @param int    $limit     [optional] <p>The maximum number of elements in the exploded
1284
     *                          collection.</p>
1285
     *
1286
     *   - If limit is set and positive, the returned collection will contain a maximum of limit elements with the last
1287
     *   element containing the rest of string.
1288
     *   - If the limit parameter is negative, all components except the last -limit are returned.
1289
     *   - If the limit parameter is zero, then this is treated as 1
1290
     *
1291
     * @psalm-mutation-free
1292
     *
1293
     * @return CollectionStringy|static[]
1294
     *                                    <p>An collection of Stringy objects.</p>
1295
     *
1296
     * @phpstan-return CollectionStringy<int,static>
1297
     */
1298
    public function explodeCollection(string $delimiter, int $limit = \PHP_INT_MAX): CollectionStringy
1299
    {
1300
        /**
1301
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to the collection class
1302
         */
1303
        return CollectionStringy::create(
1✔
1304
            $this->explode($delimiter, $limit)
1✔
1305
        );
1✔
1306
    }
1307

1308
    /**
1309
     * Create an extract from a sentence, so if the search-string was found, it try to centered in the output.
1310
     *
1311
     * EXAMPLE: <code>
1312
     * $sentence = 'This is only a Fork of Stringy, take a look at the new features.';
1313
     * s($sentence)->extractText('Stringy'); // '...Fork of Stringy...'
1314
     * </code>
1315
     *
1316
     * @param string   $search
1317
     * @param int|null $length                 [optional] <p>Default: null === text->length / 2</p>
1318
     * @param string   $replacerForSkippedText [optional] <p>Default: …</p>
1319
     *
1320
     * @psalm-mutation-free
1321
     *
1322
     * @return static
1323
     */
1324
    public function extractText(string $search = '', int $length = null, string $replacerForSkippedText = '…'): self
1325
    {
1326
        return static::create(
2✔
1327
            $this->utf8::extract_text(
2✔
1328
                $this->str,
2✔
1329
                $search,
2✔
1330
                $length,
2✔
1331
                $replacerForSkippedText,
2✔
1332
                $this->encoding
2✔
1333
            ),
2✔
1334
            $this->encoding
2✔
1335
        );
2✔
1336
    }
1337

1338
    /**
1339
     * Returns the first $n characters of the string.
1340
     *
1341
     * EXAMPLE: <code>
1342
     * s('fòôbàř')->first(3); // 'fòô'
1343
     * </code>
1344
     *
1345
     * @param int $n <p>Number of characters to retrieve from the start.</p>
1346
     *
1347
     * @psalm-mutation-free
1348
     *
1349
     * @return static
1350
     *                <p>Object with its $str being the first $n chars.</p>
1351
     */
1352
    public function first(int $n): self
1353
    {
1354
        return static::create(
37✔
1355
            $this->utf8::first_char($this->str, $n, $this->encoding),
37✔
1356
            $this->encoding
37✔
1357
        );
37✔
1358
    }
1359

1360
    /**
1361
     * Return a formatted string via sprintf + named parameters via array syntax.
1362
     *
1363
     * <p>
1364
     * <br>
1365
     * It will use "sprintf()" so you can use e.g.:
1366
     * <br>
1367
     * <br><pre>s('There are %d monkeys in the %s')->format(5, 'tree');</pre>
1368
     * <br>
1369
     * <br><pre>s('There are %2$d monkeys in the %1$s')->format('tree', 5);</pre>
1370
     * <br>
1371
     * <br>
1372
     * But you can also use named parameter via array syntax e.g.:
1373
     * <br>
1374
     * <br><pre>s('There are %:count monkeys in the %:location')->format(['count' => 5, 'location' => 'tree');</pre>
1375
     * </p>
1376
     *
1377
     * EXAMPLE: <code>
1378
     * $input = 'one: %2$d, %1$s: 2, %:text_three: %3$d';
1379
     * s($input)->format(['text_three' => '%4$s'], 'two', 1, 3, 'three'); // 'One: 1, two: 2, three: 3'
1380
     * </code>
1381
     *
1382
     * @param mixed ...$args [optional]
1383
     *
1384
     * @psalm-mutation-free
1385
     *
1386
     * @return static
1387
     *                <p>A Stringy object produced according to the formatting string
1388
     *                format.</p>
1389
     */
1390
    public function format(...$args): self
1391
    {
1392
        // init
1393
        $str = $this->str;
10✔
1394

1395
        if (\strpos($this->str, '%:') !== false) {
10✔
1396
            $offset = null;
8✔
1397
            $replacement = null;
8✔
1398
            /** @noinspection AlterInForeachInspection */
1399
            foreach ($args as $key => &$arg) {
8✔
1400
                if (!\is_array($arg)) {
8✔
1401
                    continue;
4✔
1402
                }
1403

1404
                foreach ($arg as $name => $param) {
8✔
1405
                    $name = (string) $name;
8✔
1406

1407
                    if (\strpos($name, '%:') !== 0) {
8✔
1408
                        $nameTmp = '%:' . $name;
8✔
1409
                    } else {
1410
                        $nameTmp = $name;
×
1411
                    }
1412

1413
                    if ($offset === null) {
8✔
1414
                        $offset = \strpos($str, $nameTmp);
8✔
1415
                    } else {
1416
                        $offset = \strpos($str, $nameTmp, (int) $offset + \strlen((string) $replacement));
6✔
1417
                    }
1418
                    if ($offset === false) {
8✔
1419
                        continue;
4✔
1420
                    }
1421

1422
                    unset($arg[$name]);
8✔
1423

1424
                    $str = \substr_replace($str, (string) $param, (int) $offset, \strlen($nameTmp));
8✔
1425
                }
1426

1427
                unset($args[$key]);
8✔
1428
            }
1429
        }
1430

1431
        $str = \str_replace('%:', '%%:', $str);
10✔
1432

1433
        return static::create(
10✔
1434
            \sprintf($str, ...$args),
10✔
1435
            $this->encoding
10✔
1436
        );
10✔
1437
    }
1438

1439
    /**
1440
     * Returns the encoding used by the Stringy object.
1441
     *
1442
     * EXAMPLE: <code>
1443
     * s('fòôbàř', 'UTF-8')->getEncoding(); // 'UTF-8'
1444
     * </code>
1445
     *
1446
     * @psalm-mutation-free
1447
     *
1448
     * @return string
1449
     *                <p>The current value of the $encoding property.</p>
1450
     */
1451
    public function getEncoding(): string
1452
    {
1453
        return $this->encoding;
25✔
1454
    }
1455

1456
    /**
1457
     * Returns a new ArrayIterator, thus implementing the IteratorAggregate
1458
     * interface. The ArrayIterator's constructor is passed an array of chars
1459
     * in the multibyte string. This enables the use of foreach with instances
1460
     * of Stringy\Stringy.
1461
     *
1462
     * EXAMPLE: <code>
1463
     * </code>
1464
     *
1465
     * @psalm-mutation-free
1466
     *
1467
     * @return \ArrayIterator
1468
     *                        <p>An iterator for the characters in the string.</p>
1469
     *
1470
     * @phpstan-return \ArrayIterator<array-key,string>
1471
     */
1472
    public function getIterator(): \ArrayIterator
1473
    {
1474
        return new \ArrayIterator($this->chars());
3✔
1475
    }
1476

1477
    /**
1478
     * Wrap the string after an exact number of characters.
1479
     *
1480
     * EXAMPLE: <code>
1481
     * </code>
1482
     *
1483
     * @param int    $width <p>Number of characters at which to wrap.</p>
1484
     * @param string $break [optional] <p>Character used to break the string. | Default: "\n"</p>
1485
     *
1486
     * @psalm-mutation-free
1487
     *
1488
     * @return static
1489
     */
1490
    public function hardWrap($width, $break = "\n"): self
1491
    {
1492
        return $this->lineWrap($width, $break, false);
2✔
1493
    }
1494

1495
    /**
1496
     * Returns true if the string contains a lower case char, false otherwise
1497
     *
1498
     * EXAMPLE: <code>
1499
     * s('fòôbàř')->hasLowerCase(); // true
1500
     * </code>
1501
     *
1502
     * @psalm-mutation-free
1503
     *
1504
     * @return bool
1505
     *              <p>Whether or not the string contains a lower case character.</p>
1506
     */
1507
    public function hasLowerCase(): bool
1508
    {
1509
        return $this->utf8::has_lowercase($this->str);
36✔
1510
    }
1511

1512
    /**
1513
     * Returns true if the string contains an upper case char, false otherwise.
1514
     *
1515
     * EXAMPLE: <code>
1516
     * s('fòôbàř')->hasUpperCase(); // false
1517
     * </code>
1518
     *
1519
     * @psalm-mutation-free
1520
     *
1521
     * @return bool
1522
     *              <p>Whether or not the string contains an upper case character.</p>
1523
     */
1524
    public function hasUpperCase(): bool
1525
    {
1526
        return $this->utf8::has_uppercase($this->str);
36✔
1527
    }
1528

1529
    /**
1530
     * Generate a hash value (message digest).
1531
     *
1532
     * EXAMPLE: <code>
1533
     * </code>
1534
     *
1535
     * @see https://php.net/manual/en/function.hash.php
1536
     *
1537
     * @param string $algorithm
1538
     *                          <p>Name of selected hashing algorithm (i.e. "md5", "sha256", "haval160,4", etc..)</p>
1539
     *
1540
     * @psalm-mutation-free
1541
     *
1542
     * @return static
1543
     */
1544
    public function hash($algorithm): self
1545
    {
1546
        return static::create(\hash($algorithm, $this->str), $this->encoding);
8✔
1547
    }
1548

1549
    /**
1550
     * Decode the string from hex.
1551
     *
1552
     * EXAMPLE: <code>
1553
     * </code>
1554
     *
1555
     * @psalm-mutation-free
1556
     *
1557
     * @return static
1558
     */
1559
    public function hexDecode(): self
1560
    {
1561
        $string = \preg_replace_callback(
2✔
1562
            '/\\\\x([0-9A-Fa-f]+)/',
2✔
1563
            function (array $matched) {
2✔
1564
                return (string) $this->utf8::hex_to_chr($matched[1]);
2✔
1565
            },
2✔
1566
            $this->str
2✔
1567
        );
2✔
1568

1569
        return static::create(
2✔
1570
            $string,
2✔
1571
            $this->encoding
2✔
1572
        );
2✔
1573
    }
1574

1575
    /**
1576
     * Encode string to hex.
1577
     *
1578
     * EXAMPLE: <code>
1579
     * </code>
1580
     *
1581
     * @psalm-mutation-free
1582
     *
1583
     * @return static
1584
     */
1585
    public function hexEncode(): self
1586
    {
1587
        $string = \array_reduce(
2✔
1588
            $this->chars(),
2✔
1589
            function (string $str, string $char) {
2✔
1590
                return $str . $this->utf8::chr_to_hex($char);
2✔
1591
            },
2✔
1592
            ''
2✔
1593
        );
2✔
1594

1595
        return static::create(
2✔
1596
            $string,
2✔
1597
            $this->encoding
2✔
1598
        );
2✔
1599
    }
1600

1601
    /**
1602
     * Convert all HTML entities to their applicable characters.
1603
     *
1604
     * EXAMPLE: <code>
1605
     * s('&amp;')->htmlDecode(); // '&'
1606
     * </code>
1607
     *
1608
     * @param int $flags [optional] <p>
1609
     *                   A bitmask of one or more of the following flags, which specify how to handle quotes and
1610
     *                   which document type to use. The default is ENT_COMPAT.
1611
     *                   <table>
1612
     *                   Available <i>flags</i> constants
1613
     *                   <tr valign="top">
1614
     *                   <td>Constant Name</td>
1615
     *                   <td>Description</td>
1616
     *                   </tr>
1617
     *                   <tr valign="top">
1618
     *                   <td><b>ENT_COMPAT</b></td>
1619
     *                   <td>Will convert double-quotes and leave single-quotes alone.</td>
1620
     *                   </tr>
1621
     *                   <tr valign="top">
1622
     *                   <td><b>ENT_QUOTES</b></td>
1623
     *                   <td>Will convert both double and single quotes.</td>
1624
     *                   </tr>
1625
     *                   <tr valign="top">
1626
     *                   <td><b>ENT_NOQUOTES</b></td>
1627
     *                   <td>Will leave both double and single quotes unconverted.</td>
1628
     *                   </tr>
1629
     *                   <tr valign="top">
1630
     *                   <td><b>ENT_HTML401</b></td>
1631
     *                   <td>
1632
     *                   Handle code as HTML 4.01.
1633
     *                   </td>
1634
     *                   </tr>
1635
     *                   <tr valign="top">
1636
     *                   <td><b>ENT_XML1</b></td>
1637
     *                   <td>
1638
     *                   Handle code as XML 1.
1639
     *                   </td>
1640
     *                   </tr>
1641
     *                   <tr valign="top">
1642
     *                   <td><b>ENT_XHTML</b></td>
1643
     *                   <td>
1644
     *                   Handle code as XHTML.
1645
     *                   </td>
1646
     *                   </tr>
1647
     *                   <tr valign="top">
1648
     *                   <td><b>ENT_HTML5</b></td>
1649
     *                   <td>
1650
     *                   Handle code as HTML 5.
1651
     *                   </td>
1652
     *                   </tr>
1653
     *                   </table>
1654
     *                   </p>
1655
     *
1656
     * @psalm-mutation-free
1657
     *
1658
     * @return static
1659
     *                <p>Object with the resulting $str after being html decoded.</p>
1660
     */
1661
    public function htmlDecode(int $flags = \ENT_COMPAT): self
1662
    {
1663
        return static::create(
15✔
1664
            $this->utf8::html_entity_decode(
15✔
1665
                $this->str,
15✔
1666
                $flags,
15✔
1667
                $this->encoding
15✔
1668
            ),
15✔
1669
            $this->encoding
15✔
1670
        );
15✔
1671
    }
1672

1673
    /**
1674
     * Convert all applicable characters to HTML entities.
1675
     *
1676
     * EXAMPLE: <code>
1677
     * s('&')->htmlEncode(); // '&amp;'
1678
     * </code>
1679
     *
1680
     * @param int $flags [optional] <p>
1681
     *                   A bitmask of one or more of the following flags, which specify how to handle quotes and
1682
     *                   which document type to use. The default is ENT_COMPAT.
1683
     *                   <table>
1684
     *                   Available <i>flags</i> constants
1685
     *                   <tr valign="top">
1686
     *                   <td>Constant Name</td>
1687
     *                   <td>Description</td>
1688
     *                   </tr>
1689
     *                   <tr valign="top">
1690
     *                   <td><b>ENT_COMPAT</b></td>
1691
     *                   <td>Will convert double-quotes and leave single-quotes alone.</td>
1692
     *                   </tr>
1693
     *                   <tr valign="top">
1694
     *                   <td><b>ENT_QUOTES</b></td>
1695
     *                   <td>Will convert both double and single quotes.</td>
1696
     *                   </tr>
1697
     *                   <tr valign="top">
1698
     *                   <td><b>ENT_NOQUOTES</b></td>
1699
     *                   <td>Will leave both double and single quotes unconverted.</td>
1700
     *                   </tr>
1701
     *                   <tr valign="top">
1702
     *                   <td><b>ENT_HTML401</b></td>
1703
     *                   <td>
1704
     *                   Handle code as HTML 4.01.
1705
     *                   </td>
1706
     *                   </tr>
1707
     *                   <tr valign="top">
1708
     *                   <td><b>ENT_XML1</b></td>
1709
     *                   <td>
1710
     *                   Handle code as XML 1.
1711
     *                   </td>
1712
     *                   </tr>
1713
     *                   <tr valign="top">
1714
     *                   <td><b>ENT_XHTML</b></td>
1715
     *                   <td>
1716
     *                   Handle code as XHTML.
1717
     *                   </td>
1718
     *                   </tr>
1719
     *                   <tr valign="top">
1720
     *                   <td><b>ENT_HTML5</b></td>
1721
     *                   <td>
1722
     *                   Handle code as HTML 5.
1723
     *                   </td>
1724
     *                   </tr>
1725
     *                   </table>
1726
     *                   </p>
1727
     *
1728
     * @psalm-mutation-free
1729
     *
1730
     * @return static
1731
     *                <p>Object with the resulting $str after being html encoded.</p>
1732
     */
1733
    public function htmlEncode(int $flags = \ENT_COMPAT): self
1734
    {
1735
        return static::create(
15✔
1736
            $this->utf8::htmlentities(
15✔
1737
                $this->str,
15✔
1738
                $flags,
15✔
1739
                $this->encoding
15✔
1740
            ),
15✔
1741
            $this->encoding
15✔
1742
        );
15✔
1743
    }
1744

1745
    /**
1746
     * Capitalizes the first word of the string, replaces underscores with
1747
     * spaces, and strips '_id'.
1748
     *
1749
     * EXAMPLE: <code>
1750
     * s('author_id')->humanize(); // 'Author'
1751
     * </code>
1752
     *
1753
     * @psalm-mutation-free
1754
     *
1755
     * @return static
1756
     *                <p>Object with a humanized $str.</p>
1757
     */
1758
    public function humanize(): self
1759
    {
1760
        return static::create(
9✔
1761
            $this->utf8::str_humanize($this->str),
9✔
1762
            $this->encoding
9✔
1763
        );
9✔
1764
    }
1765

1766
    /**
1767
     * Determine if the current string exists in another string. By
1768
     * default, the comparison is case-sensitive, but can be made insensitive
1769
     * by setting $caseSensitive to false.
1770
     *
1771
     * EXAMPLE: <code>
1772
     * </code>
1773
     *
1774
     * @param string $str           <p>The string to compare against.</p>
1775
     * @param bool   $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
1776
     *
1777
     * @psalm-mutation-free
1778
     *
1779
     * @return bool
1780
     */
1781
    public function in(string $str, bool $caseSensitive = true): bool
1782
    {
1783
        if ($caseSensitive) {
3✔
1784
            return \strpos($str, $this->str) !== false;
2✔
1785
        }
1786

1787
        return \stripos($str, $this->str) !== false;
1✔
1788
    }
1789

1790
    /**
1791
     * Returns the index of the first occurrence of $needle in the string,
1792
     * and false if not found. Accepts an optional offset from which to begin
1793
     * the search.
1794
     *
1795
     * EXAMPLE: <code>
1796
     * s('string')->indexOf('ing'); // 3
1797
     * </code>
1798
     *
1799
     * @param string $needle <p>Substring to look for.</p>
1800
     * @param int    $offset [optional] <p>Offset from which to search. Default: 0</p>
1801
     *
1802
     * @psalm-mutation-free
1803
     *
1804
     * @return false|int
1805
     *                   <p>The occurrence's <strong>index</strong> if found, otherwise <strong>false</strong>.</p>
1806
     */
1807
    public function indexOf(string $needle, int $offset = 0)
1808
    {
1809
        return $this->utf8::strpos(
31✔
1810
            $this->str,
31✔
1811
            $needle,
31✔
1812
            $offset,
31✔
1813
            $this->encoding
31✔
1814
        );
31✔
1815
    }
1816

1817
    /**
1818
     * Returns the index of the first occurrence of $needle in the string,
1819
     * and false if not found. Accepts an optional offset from which to begin
1820
     * the search.
1821
     *
1822
     * EXAMPLE: <code>
1823
     * s('string')->indexOfIgnoreCase('ING'); // 3
1824
     * </code>
1825
     *
1826
     * @param string $needle <p>Substring to look for.</p>
1827
     * @param int    $offset [optional] <p>Offset from which to search. Default: 0</p>
1828
     *
1829
     * @psalm-mutation-free
1830
     *
1831
     * @return false|int
1832
     *                   <p>The occurrence's <strong>index</strong> if found, otherwise <strong>false</strong>.</p>
1833
     */
1834
    public function indexOfIgnoreCase(string $needle, int $offset = 0)
1835
    {
1836
        return $this->utf8::stripos(
20✔
1837
            $this->str,
20✔
1838
            $needle,
20✔
1839
            $offset,
20✔
1840
            $this->encoding
20✔
1841
        );
20✔
1842
    }
1843

1844
    /**
1845
     * Returns the index of the last occurrence of $needle in the string,
1846
     * and false if not found. Accepts an optional offset from which to begin
1847
     * the search. Offsets may be negative to count from the last character
1848
     * in the string.
1849
     *
1850
     * EXAMPLE: <code>
1851
     * s('foobarfoo')->indexOfLast('foo'); // 10
1852
     * </code>
1853
     *
1854
     * @param string $needle <p>Substring to look for.</p>
1855
     * @param int    $offset [optional] <p>Offset from which to search. Default: 0</p>
1856
     *
1857
     * @psalm-mutation-free
1858
     *
1859
     * @return false|int
1860
     *                   <p>The last occurrence's <strong>index</strong> if found, otherwise <strong>false</strong>.</p>
1861
     */
1862
    public function indexOfLast(string $needle, int $offset = 0)
1863
    {
1864
        return $this->utf8::strrpos(
31✔
1865
            $this->str,
31✔
1866
            $needle,
31✔
1867
            $offset,
31✔
1868
            $this->encoding
31✔
1869
        );
31✔
1870
    }
1871

1872
    /**
1873
     * Returns the index of the last occurrence of $needle in the string,
1874
     * and false if not found. Accepts an optional offset from which to begin
1875
     * the search. Offsets may be negative to count from the last character
1876
     * in the string.
1877
     *
1878
     * EXAMPLE: <code>
1879
     * s('fooBarFoo')->indexOfLastIgnoreCase('foo'); // 10
1880
     * </code>
1881
     *
1882
     * @param string $needle <p>Substring to look for.</p>
1883
     * @param int    $offset [optional] <p>Offset from which to search. Default: 0</p>
1884
     *
1885
     * @psalm-mutation-free
1886
     *
1887
     * @return false|int
1888
     *                   <p>The last occurrence's <strong>index</strong> if found, otherwise <strong>false</strong>.</p>
1889
     */
1890
    public function indexOfLastIgnoreCase(string $needle, int $offset = 0)
1891
    {
1892
        return $this->utf8::strripos(
20✔
1893
            $this->str,
20✔
1894
            $needle,
20✔
1895
            $offset,
20✔
1896
            $this->encoding
20✔
1897
        );
20✔
1898
    }
1899

1900
    /**
1901
     * Inserts $substring into the string at the $index provided.
1902
     *
1903
     * EXAMPLE: <code>
1904
     * s('fòôbř')->insert('à', 4); // 'fòôbàř'
1905
     * </code>
1906
     *
1907
     * @param string $substring <p>String to be inserted.</p>
1908
     * @param int    $index     <p>The index at which to insert the substring.</p>
1909
     *
1910
     * @psalm-mutation-free
1911
     *
1912
     * @return static
1913
     *                <p>Object with the resulting $str after the insertion.</p>
1914
     */
1915
    public function insert(string $substring, int $index): self
1916
    {
1917
        return static::create(
24✔
1918
            $this->utf8::str_insert(
24✔
1919
                $this->str,
24✔
1920
                $substring,
24✔
1921
                $index,
24✔
1922
                $this->encoding
24✔
1923
            ),
24✔
1924
            $this->encoding
24✔
1925
        );
24✔
1926
    }
1927

1928
    /**
1929
     * Returns true if the string contains the $pattern, otherwise false.
1930
     *
1931
     * WARNING: Asterisks ("*") are translated into (".*") zero-or-more regular
1932
     * expression wildcards.
1933
     *
1934
     * EXAMPLE: <code>
1935
     * s('Foo\\Bar\\Lall')->is('*\\Bar\\*'); // true
1936
     * </code>
1937
     *
1938
     * @credit Originally from Laravel, thanks Taylor.
1939
     *
1940
     * @param string $pattern <p>The string or pattern to match against.</p>
1941
     *
1942
     * @psalm-mutation-free
1943
     *
1944
     * @return bool
1945
     *              <p>Whether or not we match the provided pattern.</p>
1946
     */
1947
    public function is(string $pattern): bool
1948
    {
1949
        if ($this->toString() === $pattern) {
26✔
1950
            return true;
2✔
1951
        }
1952

1953
        $quotedPattern = \preg_quote($pattern, '/');
24✔
1954
        $replaceWildCards = \str_replace('\*', '.*', $quotedPattern);
24✔
1955

1956
        return $this->matchesPattern('^' . $replaceWildCards . '\z');
24✔
1957
    }
1958

1959
    /**
1960
     * Returns true if the string contains only alphabetic chars, false otherwise.
1961
     *
1962
     * EXAMPLE: <code>
1963
     * s('丹尼爾')->isAlpha(); // true
1964
     * </code>
1965
     *
1966
     * @psalm-mutation-free
1967
     *
1968
     * @return bool
1969
     *              <p>Whether or not $str contains only alphabetic chars.</p>
1970
     */
1971
    public function isAlpha(): bool
1972
    {
1973
        return $this->utf8::is_alpha($this->str);
30✔
1974
    }
1975

1976
    /**
1977
     * Returns true if the string contains only alphabetic and numeric chars, false otherwise.
1978
     *
1979
     * EXAMPLE: <code>
1980
     * s('دانيال1')->isAlphanumeric(); // true
1981
     * </code>
1982
     *
1983
     * @psalm-mutation-free
1984
     *
1985
     * @return bool
1986
     *              <p>Whether or not $str contains only alphanumeric chars.</p>
1987
     */
1988
    public function isAlphanumeric(): bool
1989
    {
1990
        return $this->utf8::is_alphanumeric($this->str);
39✔
1991
    }
1992

1993
    /**
1994
     * Checks if a string is 7 bit ASCII.
1995
     *
1996
     * EXAMPLE: <code>s('白')->isAscii; // false</code>
1997
     *
1998
     * @psalm-mutation-free
1999
     *
2000
     * @return bool
2001
     *              <p>
2002
     *              <strong>true</strong> if it is ASCII<br>
2003
     *              <strong>false</strong> otherwise
2004
     *              </p>
2005
     *
2006
     * @noinspection GetSetMethodCorrectnessInspection
2007
     */
2008
    public function isAscii(): bool
2009
    {
2010
        return $this->utf8::is_ascii($this->str);
×
2011
    }
2012

2013
    /**
2014
     * Returns true if the string is base64 encoded, false otherwise.
2015
     *
2016
     * EXAMPLE: <code>
2017
     * s('Zm9vYmFy')->isBase64(); // true
2018
     * </code>
2019
     *
2020
     * @param bool $emptyStringIsValid
2021
     *
2022
     * @psalm-mutation-free
2023
     *
2024
     * @return bool
2025
     *              <p>Whether or not $str is base64 encoded.</p>
2026
     */
2027
    public function isBase64($emptyStringIsValid = true): bool
2028
    {
2029
        return $this->utf8::is_base64($this->str, $emptyStringIsValid);
21✔
2030
    }
2031

2032
    /**
2033
     * Check if the input is binary... (is look like a hack).
2034
     *
2035
     * EXAMPLE: <code>s(01)->isBinary(); // true</code>
2036
     *
2037
     * @psalm-mutation-free
2038
     *
2039
     * @return bool
2040
     */
2041
    public function isBinary(): bool
2042
    {
2043
        return $this->utf8::is_binary($this->str);
1✔
2044
    }
2045

2046
    /**
2047
     * Returns true if the string contains only whitespace chars, false otherwise.
2048
     *
2049
     * EXAMPLE: <code>
2050
     * s("\n\t  \v\f")->isBlank(); // true
2051
     * </code>
2052
     *
2053
     * @psalm-mutation-free
2054
     *
2055
     * @return bool
2056
     *              <p>Whether or not $str contains only whitespace characters.</p>
2057
     */
2058
    public function isBlank(): bool
2059
    {
2060
        return $this->utf8::is_blank($this->str);
45✔
2061
    }
2062

2063
    /**
2064
     * Checks if the given string is equal to any "Byte Order Mark".
2065
     *
2066
     * WARNING: Use "s::string_has_bom()" if you will check BOM in a string.
2067
     *
2068
     * EXAMPLE: <code>s->("\xef\xbb\xbf")->isBom(); // true</code>
2069
     *
2070
     * @psalm-mutation-free
2071
     *
2072
     * @return bool
2073
     *              <p><strong>true</strong> if the $utf8_chr is Byte Order Mark, <strong>false</strong> otherwise.</p>
2074
     */
2075
    public function isBom(): bool
2076
    {
2077
        return $this->utf8::is_bom($this->str);
×
2078
    }
2079

2080
    /**
2081
     * Returns true if the string contains a valid E-Mail address, false otherwise.
2082
     *
2083
     * EXAMPLE: <code>
2084
     * s('lars@moelleken.org')->isEmail(); // true
2085
     * </code>
2086
     *
2087
     * @param bool $useExampleDomainCheck   [optional] <p>Default: false</p>
2088
     * @param bool $useTypoInDomainCheck    [optional] <p>Default: false</p>
2089
     * @param bool $useTemporaryDomainCheck [optional] <p>Default: false</p>
2090
     * @param bool $useDnsCheck             [optional] <p>Default: false</p>
2091
     *
2092
     * @psalm-mutation-free
2093
     *
2094
     * @return bool
2095
     *              <p>Whether or not $str contains a valid E-Mail address.</p>
2096
     */
2097
    public function isEmail(
2098
        bool $useExampleDomainCheck = false,
2099
        bool $useTypoInDomainCheck = false,
2100
        bool $useTemporaryDomainCheck = false,
2101
        bool $useDnsCheck = false
2102
    ): bool {
2103
        /**
2104
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to the email-check class
2105
         */
2106
        return EmailCheck::isValid($this->str, $useExampleDomainCheck, $useTypoInDomainCheck, $useTemporaryDomainCheck, $useDnsCheck);
2✔
2107
    }
2108

2109
    /**
2110
     * Determine whether the string is considered to be empty.
2111
     *
2112
     * A variable is considered empty if it does not exist or if its value equals FALSE.
2113
     *
2114
     * EXAMPLE: <code>
2115
     * s('')->isEmpty(); // true
2116
     * </code>
2117
     *
2118
     * @psalm-mutation-free
2119
     *
2120
     * @return bool
2121
     *              <p>Whether or not $str is empty().</p>
2122
     */
2123
    public function isEmpty(): bool
2124
    {
2125
        return $this->utf8::is_empty($this->str);
10✔
2126
    }
2127

2128
    /**
2129
     * Determine whether the string is equals to $str.
2130
     * Alias for isEqualsCaseSensitive()
2131
     *
2132
     * EXAMPLE: <code>
2133
     * s('foo')->isEquals('foo'); // true
2134
     * </code>
2135
     *
2136
     * @param string|Stringy ...$str
2137
     *
2138
     * @psalm-mutation-free
2139
     *
2140
     * @return bool
2141
     */
2142
    public function isEquals(...$str): bool
2143
    {
2144
        return $this->isEqualsCaseSensitive(...$str);
13✔
2145
    }
2146

2147
    /**
2148
     * Determine whether the string is equals to $str.
2149
     *
2150
     * EXAMPLE: <code>
2151
     * </code>
2152
     *
2153
     * @param float|int|string|Stringy ...$str <p>The string to compare.</p>
2154
     *
2155
     * @psalm-mutation-free
2156
     *
2157
     * @return bool
2158
     *              <p>Whether or not $str is equals.</p>
2159
     */
2160
    public function isEqualsCaseInsensitive(...$str): bool
2161
    {
2162
        $strUpper = $this->toUpperCase()->str;
3✔
2163

2164
        foreach ($str as $strTmp) {
3✔
2165
            /**
2166
             * @psalm-suppress RedundantConditionGivenDocblockType - wait for union-types :)
2167
             */
2168
            if ($strTmp instanceof self) {
3✔
2169
                if ($strUpper !== $strTmp->toUpperCase()->str) {
×
2170
                    return false;
×
2171
                }
2172
            } elseif (\is_scalar($strTmp)) {
3✔
2173
                if ($strUpper !== $this->utf8::strtoupper((string) $strTmp, $this->encoding)) {
3✔
2174
                    return false;
3✔
2175
                }
2176
            } else {
2177
                throw new \InvalidArgumentException('expected: int|float|string|Stringy -> given: ' . \print_r($strTmp, true) . ' [' . \gettype($strTmp) . ']');
×
2178
            }
2179
        }
2180

2181
        return true;
3✔
2182
    }
2183

2184
    /**
2185
     * Determine whether the string is equals to $str.
2186
     *
2187
     * EXAMPLE: <code>
2188
     * </code>
2189
     *
2190
     * @param float|int|string|Stringy ...$str <p>The string to compare.</p>
2191
     *
2192
     * @psalm-mutation-free
2193
     *
2194
     * @return bool
2195
     *              <p>Whether or not $str is equals.</p>
2196
     */
2197
    public function isEqualsCaseSensitive(...$str): bool
2198
    {
2199
        foreach ($str as $strTmp) {
14✔
2200
            /**
2201
             * @psalm-suppress RedundantConditionGivenDocblockType - wait for union-types :)
2202
             */
2203
            if ($strTmp instanceof self) {
14✔
2204
                if ($this->str !== $strTmp->str) {
2✔
2205
                    return false;
2✔
2206
                }
2207
            } elseif (\is_scalar($strTmp)) {
12✔
2208
                if ($this->str !== (string) $strTmp) {
12✔
2209
                    return false;
12✔
2210
                }
2211
            } else {
2212
                throw new \InvalidArgumentException('expected: int|float|string|Stringy -> given: ' . \print_r($strTmp, true) . ' [' . \gettype($strTmp) . ']');
×
2213
            }
2214
        }
2215

2216
        return true;
3✔
2217
    }
2218

2219
    /**
2220
     * Returns true if the string contains only hexadecimal chars, false otherwise.
2221
     *
2222
     * EXAMPLE: <code>
2223
     * s('A102F')->isHexadecimal(); // true
2224
     * </code>
2225
     *
2226
     * @psalm-mutation-free
2227
     *
2228
     * @return bool
2229
     *              <p>Whether or not $str contains only hexadecimal chars.</p>
2230
     */
2231
    public function isHexadecimal(): bool
2232
    {
2233
        return $this->utf8::is_hexadecimal($this->str);
39✔
2234
    }
2235

2236
    /**
2237
     * Returns true if the string contains HTML-Tags, false otherwise.
2238
     *
2239
     * EXAMPLE: <code>
2240
     * s('<h1>foo</h1>')->isHtml(); // true
2241
     * </code>
2242
     *
2243
     * @psalm-mutation-free
2244
     *
2245
     * @return bool
2246
     *              <p>Whether or not $str contains HTML-Tags.</p>
2247
     */
2248
    public function isHtml(): bool
2249
    {
2250
        return $this->utf8::is_html($this->str);
2✔
2251
    }
2252

2253
    /**
2254
     * Returns true if the string is JSON, false otherwise. Unlike json_decode
2255
     * in PHP 5.x, this method is consistent with PHP 7 and other JSON parsers,
2256
     * in that an empty string is not considered valid JSON.
2257
     *
2258
     * EXAMPLE: <code>
2259
     * s('{"foo":"bar"}')->isJson(); // true
2260
     * </code>
2261
     *
2262
     * @param bool $onlyArrayOrObjectResultsAreValid
2263
     *
2264
     * @psalm-mutation-free
2265
     *
2266
     * @return bool
2267
     *              <p>Whether or not $str is JSON.</p>
2268
     */
2269
    public function isJson($onlyArrayOrObjectResultsAreValid = false): bool
2270
    {
2271
        /**
2272
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to vendor stuff?
2273
         */
2274
        return $this->utf8::is_json(
60✔
2275
            $this->str,
60✔
2276
            $onlyArrayOrObjectResultsAreValid
60✔
2277
        );
60✔
2278
    }
2279

2280
    /**
2281
     * Returns true if the string contains only lower case chars, false otherwise.
2282
     *
2283
     * EXAMPLE: <code>
2284
     * s('fòôbàř')->isLowerCase(); // true
2285
     * </code>
2286
     *
2287
     * @psalm-mutation-free
2288
     *
2289
     * @return bool
2290
     *              <p>Whether or not $str contains only lower case characters.</p>
2291
     */
2292
    public function isLowerCase(): bool
2293
    {
2294
        return $this->utf8::is_lowercase($this->str);
24✔
2295
    }
2296

2297
    /**
2298
     * Determine whether the string is considered to be NOT empty.
2299
     *
2300
     * A variable is considered NOT empty if it does exist or if its value equals TRUE.
2301
     *
2302
     * EXAMPLE: <code>
2303
     * s('')->isNotEmpty(); // false
2304
     * </code>
2305
     *
2306
     * @psalm-mutation-free
2307
     *
2308
     * @return bool
2309
     *              <p>Whether or not $str is empty().</p>
2310
     */
2311
    public function isNotEmpty(): bool
2312
    {
2313
        return !$this->utf8::is_empty($this->str);
10✔
2314
    }
2315

2316
    /**
2317
     * Determine if the string is composed of numeric characters.
2318
     *
2319
     * EXAMPLE: <code>
2320
     * </code>
2321
     *
2322
     * @psalm-mutation-free
2323
     *
2324
     * @return bool
2325
     */
2326
    public function isNumeric(): bool
2327
    {
2328
        return \is_numeric($this->str);
4✔
2329
    }
2330

2331
    /**
2332
     * Determine if the string is composed of printable (non-invisible) characters.
2333
     *
2334
     * EXAMPLE: <code>
2335
     * </code>
2336
     *
2337
     * @psalm-mutation-free
2338
     *
2339
     * @return bool
2340
     */
2341
    public function isPrintable(): bool
2342
    {
2343
        return $this->utf8::is_printable($this->str);
3✔
2344
    }
2345

2346
    /**
2347
     * Determine if the string is composed of punctuation characters.
2348
     *
2349
     * EXAMPLE: <code>
2350
     * </code>
2351
     *
2352
     * @psalm-mutation-free
2353
     *
2354
     * @return bool
2355
     */
2356
    public function isPunctuation(): bool
2357
    {
2358
        return $this->utf8::is_punctuation($this->str);
3✔
2359
    }
2360

2361
    /**
2362
     * Returns true if the string is serialized, false otherwise.
2363
     *
2364
     * EXAMPLE: <code>
2365
     * s('a:1:{s:3:"foo";s:3:"bar";}')->isSerialized(); // true
2366
     * </code>
2367
     *
2368
     * @psalm-mutation-free
2369
     *
2370
     * @return bool
2371
     *              <p>Whether or not $str is serialized.</p>
2372
     */
2373
    public function isSerialized(): bool
2374
    {
2375
        return $this->utf8::is_serialized($this->str);
21✔
2376
    }
2377

2378
    /**
2379
     * Check if two strings are similar.
2380
     *
2381
     * EXAMPLE: <code>
2382
     * </code>
2383
     *
2384
     * @param string $str                     <p>The string to compare against.</p>
2385
     * @param float  $minPercentForSimilarity [optional] <p>The percentage of needed similarity. | Default: 80%</p>
2386
     *
2387
     * @psalm-mutation-free
2388
     *
2389
     * @return bool
2390
     */
2391
    public function isSimilar(string $str, float $minPercentForSimilarity = 80.0): bool
2392
    {
2393
        return $this->similarity($str) >= $minPercentForSimilarity;
2✔
2394
    }
2395

2396
    /**
2397
     * Returns true if the string contains only lower case chars, false
2398
     * otherwise.
2399
     *
2400
     * EXAMPLE: <code>
2401
     * s('FÒÔBÀŘ')->isUpperCase(); // true
2402
     * </code>
2403
     *
2404
     * @psalm-mutation-free
2405
     *
2406
     * @return bool
2407
     *              <p>Whether or not $str contains only lower case characters.</p>
2408
     */
2409
    public function isUpperCase(): bool
2410
    {
2411
        return $this->utf8::is_uppercase($this->str);
24✔
2412
    }
2413

2414
    /**
2415
     * /**
2416
     * Check if $url is an correct url.
2417
     *
2418
     * @param bool $disallow_localhost
2419
     *
2420
     * @psalm-mutation-free
2421
     *
2422
     * @return bool
2423
     */
2424
    public function isUrl(bool $disallow_localhost = false): bool
2425
    {
2426
        return $this->utf8::is_url($this->str, $disallow_localhost);
×
2427
    }
2428

2429
    /**
2430
     * Check if the string is UTF-16.
2431
     *
2432
     * @psalm-mutation-free
2433
     *
2434
     * @return false|int
2435
     *                   <strong>false</strong> if is't not UTF-16,<br>
2436
     *                   <strong>1</strong> for UTF-16LE,<br>
2437
     *                   <strong>2</strong> for UTF-16BE
2438
     */
2439
    public function isUtf16()
2440
    {
2441
        return $this->utf8::is_utf16($this->str);
×
2442
    }
2443

2444
    /**
2445
     * Check if the string is UTF-32.
2446
     *
2447
     * @psalm-mutation-free
2448
     *
2449
     * @return false|int
2450
     *                   <strong>false</strong> if is't not UTF-32,<br>
2451
     *                   <strong>1</strong> for UTF-32LE,<br>
2452
     *                   <strong>2</strong> for UTF-32BE
2453
     */
2454
    public function isUtf32()
2455
    {
2456
        return $this->utf8::is_utf32($this->str);
×
2457
    }
2458

2459
    /**
2460
     * Checks whether the passed input contains only byte sequences that appear valid UTF-8.
2461
     *
2462
     * EXAMPLE: <code>
2463
     * s('Iñtërnâtiônàlizætiøn')->isUtf8(); // true
2464
     * //
2465
     * s("Iñtërnâtiônàlizætiøn\xA0\xA1")->isUtf8(); // false
2466
     * </code>
2467
     *
2468
     * @param bool $strict <p>Check also if the string is not UTF-16 or UTF-32.</p>
2469
     *
2470
     * @psalm-mutation-free
2471
     *
2472
     * @return bool
2473
     */
2474
    public function isUtf8(bool $strict = false): bool
2475
    {
2476
        return $this->utf8::is_utf8($this->str, $strict);
×
2477
    }
2478

2479
    /**
2480
     * Returns true if the string contains only whitespace chars, false otherwise.
2481
     *
2482
     * EXAMPLE: <code>
2483
     * </code>
2484
     *
2485
     * @psalm-mutation-free
2486
     *
2487
     * @return bool
2488
     *              <p>Whether or not $str contains only whitespace characters.</p>
2489
     */
2490
    public function isWhitespace(): bool
2491
    {
2492
        return $this->isBlank();
30✔
2493
    }
2494

2495
    /**
2496
     * Returns value which can be serialized by json_encode().
2497
     *
2498
     * EXAMPLE: <code>
2499
     * </code>
2500
     *
2501
     * @noinspection ReturnTypeCanBeDeclaredInspection
2502
     *
2503
     * @psalm-mutation-free
2504
     *
2505
     * @return string The current value of the $str property
2506
     */
2507
    #[\ReturnTypeWillChange]
2508
    public function jsonSerialize()
2509
    {
2510
        return (string) $this;
2✔
2511
    }
2512

2513
    /**
2514
     * Convert the string to kebab-case.
2515
     *
2516
     * EXAMPLE: <code>
2517
     * </code>
2518
     *
2519
     * @psalm-mutation-free
2520
     *
2521
     * @return static
2522
     */
2523
    public function kebabCase(): self
2524
    {
2525
        $words = \array_map(
3✔
2526
            static function (self $word) {
3✔
2527
                return $word->toLowerCase();
3✔
2528
            },
3✔
2529
            $this->words('', true)
3✔
2530
        );
3✔
2531

2532
        return new static(\implode('-', $words), $this->encoding);
3✔
2533
    }
2534

2535
    /**
2536
     * Returns the last $n characters of the string.
2537
     *
2538
     * EXAMPLE: <code>
2539
     * s('fòôbàř')->last(3); // 'bàř'
2540
     * </code>
2541
     *
2542
     * @param int $n <p>Number of characters to retrieve from the end.</p>
2543
     *
2544
     * @psalm-mutation-free
2545
     *
2546
     * @return static
2547
     *                <p>Object with its $str being the last $n chars.</p>
2548
     */
2549
    public function last(int $n): self
2550
    {
2551
        return static::create(
36✔
2552
            $this->utf8::str_last_char(
36✔
2553
                $this->str,
36✔
2554
                $n,
36✔
2555
                $this->encoding
36✔
2556
            ),
36✔
2557
            $this->encoding
36✔
2558
        );
36✔
2559
    }
2560

2561
    /**
2562
     * Gets the substring after (or before via "$beforeNeedle") the last occurrence of the "$needle".
2563
     * If no match is found returns new empty Stringy object.
2564
     *
2565
     * EXAMPLE: <code>
2566
     * </code>
2567
     *
2568
     * @param string $needle       <p>The string to look for.</p>
2569
     * @param bool   $beforeNeedle [optional] <p>Default: false</p>
2570
     *
2571
     * @psalm-mutation-free
2572
     *
2573
     * @return static
2574
     */
2575
    public function lastSubstringOf(string $needle, bool $beforeNeedle = false): self
2576
    {
2577
        return static::create(
4✔
2578
            $this->utf8::str_substr_last(
4✔
2579
                $this->str,
4✔
2580
                $needle,
4✔
2581
                $beforeNeedle,
4✔
2582
                $this->encoding
4✔
2583
            ),
4✔
2584
            $this->encoding
4✔
2585
        );
4✔
2586
    }
2587

2588
    /**
2589
     * Gets the substring after (or before via "$beforeNeedle") the last occurrence of the "$needle".
2590
     * If no match is found returns new empty Stringy object.
2591
     *
2592
     * EXAMPLE: <code>
2593
     * </code>
2594
     *
2595
     * @param string $needle       <p>The string to look for.</p>
2596
     * @param bool   $beforeNeedle [optional] <p>Default: false</p>
2597
     *
2598
     * @psalm-mutation-free
2599
     *
2600
     * @return static
2601
     */
2602
    public function lastSubstringOfIgnoreCase(string $needle, bool $beforeNeedle = false): self
2603
    {
2604
        return static::create(
2✔
2605
            $this->utf8::str_isubstr_last(
2✔
2606
                $this->str,
2✔
2607
                $needle,
2✔
2608
                $beforeNeedle,
2✔
2609
                $this->encoding
2✔
2610
            ),
2✔
2611
            $this->encoding
2✔
2612
        );
2✔
2613
    }
2614

2615
    /**
2616
     * Returns the length of the string.
2617
     *
2618
     * EXAMPLE: <code>
2619
     * s('fòôbàř')->length(); // 6
2620
     * </code>
2621
     *
2622
     * @psalm-mutation-free
2623
     *
2624
     * @return int
2625
     *             <p>The number of characters in $str given the encoding.</p>
2626
     */
2627
    public function length(): int
2628
    {
2629
        return (int) $this->utf8::strlen($this->str, $this->encoding);
17✔
2630
    }
2631

2632
    /**
2633
     * Line-Wrap the string after $limit, but also after the next word.
2634
     *
2635
     * EXAMPLE: <code>
2636
     * </code>
2637
     *
2638
     * @param int         $limit           [optional] <p>The column width.</p>
2639
     * @param string      $break           [optional] <p>The line is broken using the optional break parameter.</p>
2640
     * @param bool        $add_final_break [optional] <p>
2641
     *                                     If this flag is true, then the method will add a $break at the end
2642
     *                                     of the result string.
2643
     *                                     </p>
2644
     * @param string|null $delimiter       [optional] <p>
2645
     *                                     You can change the default behavior, where we split the string by newline.
2646
     *                                     </p>
2647
     *
2648
     * @psalm-mutation-free
2649
     *
2650
     * @return static
2651
     */
2652
    public function lineWrap(
2653
        int $limit,
2654
        string $break = "\n",
2655
        bool $add_final_break = true,
2656
        string $delimiter = null
2657
    ): self {
2658
        return static::create(
3✔
2659
            $this->utf8::wordwrap_per_line(
3✔
2660
                $this->str,
3✔
2661
                $limit,
3✔
2662
                $break,
3✔
2663
                true,
3✔
2664
                $add_final_break,
3✔
2665
                $delimiter
3✔
2666
            ),
3✔
2667
            $this->encoding
3✔
2668
        );
3✔
2669
    }
2670

2671
    /**
2672
     * Line-Wrap the string after $limit, but also after the next word.
2673
     *
2674
     * EXAMPLE: <code>
2675
     * </code>
2676
     *
2677
     * @param int         $limit           [optional] <p>The column width.</p>
2678
     * @param string      $break           [optional] <p>The line is broken using the optional break parameter.</p>
2679
     * @param bool        $add_final_break [optional] <p>
2680
     *                                     If this flag is true, then the method will add a $break at the end
2681
     *                                     of the result string.
2682
     *                                     </p>
2683
     * @param string|null $delimiter       [optional] <p>
2684
     *                                     You can change the default behavior, where we split the string by newline.
2685
     *                                     </p>
2686
     *
2687
     * @psalm-mutation-free
2688
     *
2689
     * @return static
2690
     */
2691
    public function lineWrapAfterWord(
2692
        int $limit,
2693
        string $break = "\n",
2694
        bool $add_final_break = true,
2695
        string $delimiter = null
2696
    ): self {
2697
        return static::create(
4✔
2698
            $this->utf8::wordwrap_per_line(
4✔
2699
                $this->str,
4✔
2700
                $limit,
4✔
2701
                $break,
4✔
2702
                false,
4✔
2703
                $add_final_break,
4✔
2704
                $delimiter
4✔
2705
            ),
4✔
2706
            $this->encoding
4✔
2707
        );
4✔
2708
    }
2709

2710
    /**
2711
     * Splits on newlines and carriage returns, returning an array of Stringy
2712
     * objects corresponding to the lines in the string.
2713
     *
2714
     * EXAMPLE: <code>
2715
     * s("fòô\r\nbàř\n")->lines(); // ['fòô', 'bàř', '']
2716
     * </code>
2717
     *
2718
     * @psalm-mutation-free
2719
     *
2720
     * @return static[]
2721
     *                  <p>An array of Stringy objects.</p>
2722
     *
2723
     * @phpstan-return array<int,static>
2724
     */
2725
    public function lines(): array
2726
    {
2727
        if ($this->str === '') {
51✔
2728
            return [static::create('')];
3✔
2729
        }
2730

2731
        $strings = $this->utf8::str_to_lines($this->str);
48✔
2732
        /** @noinspection AlterInForeachInspection */
2733
        foreach ($strings as &$str) {
48✔
2734
            $str = static::create($str, $this->encoding);
48✔
2735
        }
2736

2737
        /** @noinspection PhpSillyAssignmentInspection */
2738
        /** @var static[] $strings */
2739
        $strings = $strings;
48✔
2740

2741
        return $strings;
48✔
2742
    }
2743

2744
    /**
2745
     * Splits on newlines and carriage returns, returning an array of Stringy
2746
     * objects corresponding to the lines in the string.
2747
     *
2748
     * EXAMPLE: <code>
2749
     * </code>
2750
     *
2751
     * @psalm-mutation-free
2752
     *
2753
     * @return CollectionStringy|static[]
2754
     *                                    <p>An collection of Stringy objects.</p>
2755
     *
2756
     * @phpstan-return CollectionStringy<int,static>
2757
     */
2758
    public function linesCollection(): CollectionStringy
2759
    {
2760
        /**
2761
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to the collection class
2762
         */
2763
        return CollectionStringy::create(
34✔
2764
            $this->lines()
34✔
2765
        );
34✔
2766
    }
2767

2768
    /**
2769
     * Returns the longest common prefix between the string and $otherStr.
2770
     *
2771
     * EXAMPLE: <code>
2772
     * s('foobar')->longestCommonPrefix('foobaz'); // 'fooba'
2773
     * </code>
2774
     *
2775
     * @param string $otherStr <p>Second string for comparison.</p>
2776
     *
2777
     * @psalm-mutation-free
2778
     *
2779
     * @return static
2780
     *                <p>Object with its $str being the longest common prefix.</p>
2781
     */
2782
    public function longestCommonPrefix(string $otherStr): self
2783
    {
2784
        return static::create(
30✔
2785
            $this->utf8::str_longest_common_prefix(
30✔
2786
                $this->str,
30✔
2787
                $otherStr,
30✔
2788
                $this->encoding
30✔
2789
            ),
30✔
2790
            $this->encoding
30✔
2791
        );
30✔
2792
    }
2793

2794
    /**
2795
     * Returns the longest common substring between the string and $otherStr.
2796
     * In the case of ties, it returns that which occurs first.
2797
     *
2798
     * EXAMPLE: <code>
2799
     * s('foobar')->longestCommonSubstring('boofar'); // 'oo'
2800
     * </code>
2801
     *
2802
     * @param string $otherStr <p>Second string for comparison.</p>
2803
     *
2804
     * @psalm-mutation-free
2805
     *
2806
     * @return static
2807
     *                <p>Object with its $str being the longest common substring.</p>
2808
     */
2809
    public function longestCommonSubstring(string $otherStr): self
2810
    {
2811
        return static::create(
30✔
2812
            $this->utf8::str_longest_common_substring(
30✔
2813
                $this->str,
30✔
2814
                $otherStr,
30✔
2815
                $this->encoding
30✔
2816
            ),
30✔
2817
            $this->encoding
30✔
2818
        );
30✔
2819
    }
2820

2821
    /**
2822
     * Returns the longest common suffix between the string and $otherStr.
2823
     *
2824
     * EXAMPLE: <code>
2825
     * s('fòôbàř')->longestCommonSuffix('fòrbàř'); // 'bàř'
2826
     * </code>
2827
     *
2828
     * @param string $otherStr <p>Second string for comparison.</p>
2829
     *
2830
     * @psalm-mutation-free
2831
     *
2832
     * @return static
2833
     *                <p>Object with its $str being the longest common suffix.</p>
2834
     */
2835
    public function longestCommonSuffix(string $otherStr): self
2836
    {
2837
        return static::create(
30✔
2838
            $this->utf8::str_longest_common_suffix(
30✔
2839
                $this->str,
30✔
2840
                $otherStr,
30✔
2841
                $this->encoding
30✔
2842
            ),
30✔
2843
            $this->encoding
30✔
2844
        );
30✔
2845
    }
2846

2847
    /**
2848
     * Converts the first character of the string to lower case.
2849
     *
2850
     * EXAMPLE: <code>
2851
     * s('Σ Foo')->lowerCaseFirst(); // 'σ Foo'
2852
     * </code>
2853
     *
2854
     * @psalm-mutation-free
2855
     *
2856
     * @return static
2857
     *                <p>Object with the first character of $str being lower case.</p>
2858
     */
2859
    public function lowerCaseFirst(): self
2860
    {
2861
        return static::create(
16✔
2862
            $this->utf8::lcfirst($this->str, $this->encoding),
16✔
2863
            $this->encoding
16✔
2864
        );
16✔
2865
    }
2866

2867
    /**
2868
     * Determine if the string matches another string regardless of case.
2869
     * Alias for isEqualsCaseInsensitive()
2870
     *
2871
     * EXAMPLE: <code>
2872
     * </code>
2873
     *
2874
     * @psalm-mutation-free
2875
     *
2876
     * @param string|Stringy ...$str
2877
     *                               <p>The string to compare against.</p>
2878
     *
2879
     * @psalm-mutation-free
2880
     *
2881
     * @return bool
2882
     */
2883
    public function matchCaseInsensitive(...$str): bool
2884
    {
2885
        return $this->isEqualsCaseInsensitive(...$str);
3✔
2886
    }
2887

2888
    /**
2889
     * Determine if the string matches another string.
2890
     * Alias for isEqualsCaseSensitive()
2891
     *
2892
     * EXAMPLE: <code>
2893
     * </code>
2894
     *
2895
     * @psalm-mutation-free
2896
     *
2897
     * @param string|Stringy ...$str
2898
     *                               <p>The string to compare against.</p>
2899
     *
2900
     * @psalm-mutation-free
2901
     *
2902
     * @return bool
2903
     */
2904
    public function matchCaseSensitive(...$str): bool
2905
    {
2906
        return $this->isEqualsCaseSensitive(...$str);
7✔
2907
    }
2908

2909
    /**
2910
     * Create a md5 hash from the current string.
2911
     *
2912
     * @psalm-mutation-free
2913
     *
2914
     * @return static
2915
     */
2916
    public function md5(): self
2917
    {
2918
        return static::create($this->hash('md5'), $this->encoding);
2✔
2919
    }
2920

2921
    /**
2922
     * Replace all breaks [<br> | \r\n | \r | \n | ...] into "<br>".
2923
     *
2924
     * EXAMPLE: <code>
2925
     * </code>
2926
     *
2927
     * @return static
2928
     */
2929
    public function newLineToHtmlBreak(): self
2930
    {
2931
        return $this->removeHtmlBreak('<br>');
1✔
2932
    }
2933

2934
    /**
2935
     * Get every nth character of the string.
2936
     *
2937
     * EXAMPLE: <code>
2938
     * </code>
2939
     *
2940
     * @param int $step   <p>The number of characters to step.</p>
2941
     * @param int $offset [optional] <p>The string offset to start at.</p>
2942
     *
2943
     * @psalm-mutation-free
2944
     *
2945
     * @return static
2946
     */
2947
    public function nth(int $step, int $offset = 0): self
2948
    {
2949
        $length = $step - 1;
4✔
2950
        $substring = $this->substr($offset)->toString();
4✔
2951

2952
        if ($substring === '') {
4✔
2953
            return new static('', $this->encoding);
×
2954
        }
2955

2956
        \preg_match_all(
4✔
2957
            "/(?:^|(?:.|\p{L}|\w){" . $length . "})(.|\p{L}|\w)/u",
4✔
2958
            $substring,
4✔
2959
            $matches
4✔
2960
        );
4✔
2961

2962
        return new static(\implode('', $matches[1] ?? []), $this->encoding);
4✔
2963
    }
2964

2965
    /**
2966
     * Returns the integer value of the current string.
2967
     *
2968
     * EXAMPLE: <code>
2969
     * s('foo1 ba2r')->extractIntegers(); // '12'
2970
     * </code>
2971
     *
2972
     * @psalm-mutation-free
2973
     *
2974
     * @return static
2975
     */
2976
    public function extractIntegers(): self
2977
    {
2978
        if ($this->str === '') {
1✔
2979
            return new static('', $this->encoding);
1✔
2980
        }
2981

2982
        \preg_match_all('/(?<integers>\d+)/', $this->str, $matches);
1✔
2983

2984
        return static::create(
1✔
2985
            \implode('', $matches['integers'] ?? []),
1✔
2986
            $this->encoding
1✔
2987
        );
1✔
2988
    }
2989

2990
    /**
2991
     * Returns the special chars of the current string.
2992
     *
2993
     * EXAMPLE: <code>
2994
     * s('foo1 ba2!r')->extractSpecialCharacters(); // '!'
2995
     * </code>
2996
     *
2997
     * @psalm-mutation-free
2998
     *
2999
     * @return static
3000
     */
3001
    public function extractSpecialCharacters(): self
3002
    {
3003
        if ($this->str === '') {
1✔
3004
            return new static('', $this->encoding);
1✔
3005
        }
3006

3007
        // no letter, no digit, no space
3008
        \preg_match_all('/((?![\p{L}0-9\s]+).)/u', $this->str, $matches);
1✔
3009

3010
        return static::create(
1✔
3011
            \implode('', $matches[0] ?? []),
1✔
3012
            $this->encoding
1✔
3013
        );
1✔
3014
    }
3015

3016
    /**
3017
     * Returns whether or not a character exists at an index. Offsets may be
3018
     * negative to count from the last character in the string. Implements
3019
     * part of the ArrayAccess interface.
3020
     *
3021
     * EXAMPLE: <code>
3022
     * </code>
3023
     *
3024
     * @param int $offset <p>The index to check.</p>
3025
     *
3026
     * @psalm-mutation-free
3027
     *
3028
     * @return bool
3029
     *              <p>Whether or not the index exists.</p>
3030
     */
3031
    public function offsetExists($offset): bool
3032
    {
3033
        return $this->utf8::str_offset_exists(
18✔
3034
            $this->str,
18✔
3035
            $offset,
18✔
3036
            $this->encoding
18✔
3037
        );
18✔
3038
    }
3039

3040
    /**
3041
     * Returns the character at the given index. Offsets may be negative to
3042
     * count from the last character in the string. Implements part of the
3043
     * ArrayAccess interface, and throws an OutOfBoundsException if the index
3044
     * does not exist.
3045
     *
3046
     * EXAMPLE: <code>
3047
     * </code>
3048
     *
3049
     * @param int $offset <p>The <strong>index</strong> from which to retrieve the char.</p>
3050
     *
3051
     * @throws \OutOfBoundsException
3052
     *                               <p>If the positive or negative offset does not exist.</p>
3053
     *
3054
     * @return string
3055
     *                <p>The character at the specified index.</p>
3056
     *
3057
     * @psalm-mutation-free
3058
     */
3059
    public function offsetGet($offset): string
3060
    {
3061
        return $this->utf8::str_offset_get($this->str, $offset, $this->encoding);
6✔
3062
    }
3063

3064
    /**
3065
     * Implements part of the ArrayAccess interface, but throws an exception
3066
     * when called. This maintains the immutability of Stringy objects.
3067
     *
3068
     * EXAMPLE: <code>
3069
     * </code>
3070
     *
3071
     * @param int   $offset <p>The index of the character.</p>
3072
     * @param mixed $value  <p>Value to set.</p>
3073
     *
3074
     * @throws \Exception
3075
     *                    <p>When called.</p>
3076
     *
3077
     * @return void
3078
     */
3079
    #[\ReturnTypeWillChange]
3080
    public function offsetSet($offset, $value)
3081
    {
3082
        // Stringy is immutable, cannot directly set char
3083
        throw new \Exception('Stringy object is immutable, cannot modify char');
3✔
3084
    }
3085

3086
    /**
3087
     * Implements part of the ArrayAccess interface, but throws an exception
3088
     * when called. This maintains the immutability of Stringy objects.
3089
     *
3090
     * EXAMPLE: <code>
3091
     * </code>
3092
     *
3093
     * @param int $offset <p>The index of the character.</p>
3094
     *
3095
     * @throws \Exception
3096
     *                    <p>When called.</p>
3097
     *
3098
     * @return void
3099
     */
3100
    #[\ReturnTypeWillChange]
3101
    public function offsetUnset($offset)
3102
    {
3103
        // Don't allow directly modifying the string
3104
        throw new \Exception('Stringy object is immutable, cannot unset char');
3✔
3105
    }
3106

3107
    /**
3108
     * Pads the string to a given length with $padStr. If length is less than
3109
     * or equal to the length of the string, no padding takes places. The
3110
     * default string used for padding is a space, and the default type (one of
3111
     * 'left', 'right', 'both') is 'right'. Throws an InvalidArgumentException
3112
     * if $padType isn't one of those 3 values.
3113
     *
3114
     * EXAMPLE: <code>
3115
     * s('fòôbàř')->pad(9, '-/', 'left'); // '-/-fòôbàř'
3116
     * </code>
3117
     *
3118
     * @param int    $length  <p>Desired string length after padding.</p>
3119
     * @param string $padStr  [optional] <p>String used to pad, defaults to space. Default: ' '</p>
3120
     * @param string $padType [optional] <p>One of 'left', 'right', 'both'. Default: 'right'</p>
3121
     *
3122
     * @throws \InvalidArgumentException
3123
     *                                   <p>If $padType isn't one of 'right', 'left' or 'both'.</p>
3124
     *
3125
     * @return static
3126
     *                <p>Object with a padded $str.</p>
3127
     *
3128
     * @psalm-mutation-free
3129
     */
3130
    public function pad(int $length, string $padStr = ' ', string $padType = 'right'): self
3131
    {
3132
        return static::create(
39✔
3133
            $this->utf8::str_pad(
39✔
3134
                $this->str,
39✔
3135
                $length,
39✔
3136
                $padStr,
39✔
3137
                $padType,
39✔
3138
                $this->encoding
39✔
3139
            )
39✔
3140
        );
39✔
3141
    }
3142

3143
    /**
3144
     * Returns a new string of a given length such that both sides of the
3145
     * string are padded. Alias for pad() with a $padType of 'both'.
3146
     *
3147
     * EXAMPLE: <code>
3148
     * s('foo bar')->padBoth(9, ' '); // ' foo bar '
3149
     * </code>
3150
     *
3151
     * @param int    $length <p>Desired string length after padding.</p>
3152
     * @param string $padStr [optional] <p>String used to pad, defaults to space. Default: ' '</p>
3153
     *
3154
     * @psalm-mutation-free
3155
     *
3156
     * @return static
3157
     *                <p>String with padding applied.</p>
3158
     */
3159
    public function padBoth(int $length, string $padStr = ' '): self
3160
    {
3161
        return static::create(
33✔
3162
            $this->utf8::str_pad_both(
33✔
3163
                $this->str,
33✔
3164
                $length,
33✔
3165
                $padStr,
33✔
3166
                $this->encoding
33✔
3167
            )
33✔
3168
        );
33✔
3169
    }
3170

3171
    /**
3172
     * Returns a new string of a given length such that the beginning of the
3173
     * string is padded. Alias for pad() with a $padType of 'left'.
3174
     *
3175
     * EXAMPLE: <code>
3176
     * s('foo bar')->padLeft(9, ' '); // '  foo bar'
3177
     * </code>
3178
     *
3179
     * @param int    $length <p>Desired string length after padding.</p>
3180
     * @param string $padStr [optional] <p>String used to pad, defaults to space. Default: ' '</p>
3181
     *
3182
     * @psalm-mutation-free
3183
     *
3184
     * @return static
3185
     *                <p>String with left padding.</p>
3186
     */
3187
    public function padLeft(int $length, string $padStr = ' '): self
3188
    {
3189
        return static::create(
21✔
3190
            $this->utf8::str_pad_left(
21✔
3191
                $this->str,
21✔
3192
                $length,
21✔
3193
                $padStr,
21✔
3194
                $this->encoding
21✔
3195
            )
21✔
3196
        );
21✔
3197
    }
3198

3199
    /**
3200
     * Returns a new string of a given length such that the end of the string
3201
     * is padded. Alias for pad() with a $padType of 'right'.
3202
     *
3203
     * EXAMPLE: <code>
3204
     * s('foo bar')->padRight(10, '_*'); // 'foo bar_*_'
3205
     * </code>
3206
     *
3207
     * @param int    $length <p>Desired string length after padding.</p>
3208
     * @param string $padStr [optional] <p>String used to pad, defaults to space. Default: ' '</p>
3209
     *
3210
     * @psalm-mutation-free
3211
     *
3212
     * @return static
3213
     *                <p>String with right padding.</p>
3214
     */
3215
    public function padRight(int $length, string $padStr = ' '): self
3216
    {
3217
        return static::create(
21✔
3218
            $this->utf8::str_pad_right(
21✔
3219
                $this->str,
21✔
3220
                $length,
21✔
3221
                $padStr,
21✔
3222
                $this->encoding
21✔
3223
            )
21✔
3224
        );
21✔
3225
    }
3226

3227
    /**
3228
     * Convert the string to PascalCase.
3229
     * Alias for studlyCase()
3230
     *
3231
     * EXAMPLE: <code>
3232
     * </code>
3233
     *
3234
     * @psalm-mutation-free
3235
     *
3236
     * @return static
3237
     */
3238
    public function pascalCase(): self
3239
    {
3240
        return $this->studlyCase();
3✔
3241
    }
3242

3243
    /**
3244
     * Returns a new string starting with $prefix.
3245
     *
3246
     * EXAMPLE: <code>
3247
     * s('bàř')->prepend('fòô'); // 'fòôbàř'
3248
     * </code>
3249
     *
3250
     * @param string ...$prefix <p>The string to append.</p>
3251
     *
3252
     * @psalm-mutation-free
3253
     *
3254
     * @return static
3255
     *                <p>Object with appended $prefix.</p>
3256
     */
3257
    public function prepend(string ...$prefix): self
3258
    {
3259
        if (\count($prefix) <= 1) {
8✔
3260
            $prefix = $prefix[0];
6✔
3261
        } else {
3262
            $prefix = \implode('', $prefix);
2✔
3263
        }
3264

3265
        return static::create($prefix . $this->str, $this->encoding);
8✔
3266
    }
3267

3268
    /**
3269
     * Returns a new string starting with $prefix.
3270
     *
3271
     * EXAMPLE: <code>
3272
     * </code>
3273
     *
3274
     * @param CollectionStringy|static ...$prefix <p>The Stringy objects to append.</p>
3275
     *
3276
     * @phpstan-param CollectionStringy<int,static>|static ...$prefix
3277
     *
3278
     * @psalm-mutation-free
3279
     *
3280
     * @return static
3281
     *                <p>Object with appended $prefix.</p>
3282
     */
3283
    public function prependStringy(...$prefix): self
3284
    {
3285
        $prefixStr = '';
1✔
3286
        foreach ($prefix as $prefixTmp) {
1✔
3287
            if ($prefixTmp instanceof CollectionStringy) {
1✔
3288
                $prefixStr .= $prefixTmp->implode('');
1✔
3289
            } else {
3290
                $prefixStr .= $prefixTmp->toString();
1✔
3291
            }
3292
        }
3293

3294
        return static::create($prefixStr . $this->str, $this->encoding);
1✔
3295
    }
3296

3297
    /**
3298
     * Replaces all occurrences of $pattern in $str by $replacement.
3299
     *
3300
     * EXAMPLE: <code>
3301
     * s('fòô ')->regexReplace('f[òô]+\s', 'bàř'); // 'bàř'
3302
     * s('fò')->regexReplace('(ò)', '\\1ô'); // 'fòô'
3303
     * </code>
3304
     *
3305
     * @param string $pattern     <p>The regular expression pattern.</p>
3306
     * @param string $replacement <p>The string to replace with.</p>
3307
     * @param string $options     [optional] <p>Matching conditions to be used.</p>
3308
     * @param string $delimiter   [optional] <p>Delimiter the the regex. Default: '/'</p>
3309
     *
3310
     * @psalm-mutation-free
3311
     *
3312
     * @return static
3313
     *                <p>Object with the result2ing $str after the replacements.</p>
3314
     */
3315
    public function regexReplace(
3316
        string $pattern,
3317
        string $replacement,
3318
        string $options = '',
3319
        string $delimiter = '/'
3320
    ): self {
3321
        return static::create(
29✔
3322
            $this->utf8::regex_replace(
29✔
3323
                $this->str,
29✔
3324
                $pattern,
29✔
3325
                $replacement,
29✔
3326
                $options,
29✔
3327
                $delimiter
29✔
3328
            ),
29✔
3329
            $this->encoding
29✔
3330
        );
29✔
3331
    }
3332

3333
    /**
3334
     * Remove html via "strip_tags()" from the string.
3335
     *
3336
     * EXAMPLE: <code>
3337
     * s('řàb <ô>òf\', ô<br/>foo <a href="#">lall</a>')->removeHtml('<br><br/>'); // 'řàb òf\', ô<br/>foo lall'
3338
     * </code>
3339
     *
3340
     * @param string $allowableTags [optional] <p>You can use the optional second parameter to specify tags which should
3341
     *                              not be stripped. Default: null
3342
     *                              </p>
3343
     *
3344
     * @psalm-mutation-free
3345
     *
3346
     * @return static
3347
     */
3348
    public function removeHtml(string $allowableTags = ''): self
3349
    {
3350
        return static::create(
12✔
3351
            $this->utf8::remove_html($this->str, $allowableTags),
12✔
3352
            $this->encoding
12✔
3353
        );
12✔
3354
    }
3355

3356
    /**
3357
     * Remove all breaks [<br> | \r\n | \r | \n | ...] from the string.
3358
     *
3359
     * EXAMPLE: <code>
3360
     * s('řàb <ô>òf\', ô<br/>foo <a href="#">lall</a>')->removeHtmlBreak(''); // 'řàb <ô>òf\', ô< foo <a href="#">lall</a>'
3361
     * </code>
3362
     *
3363
     * @param string $replacement [optional] <p>Default is a empty string.</p>
3364
     *
3365
     * @psalm-mutation-free
3366
     *
3367
     * @return static
3368
     */
3369
    public function removeHtmlBreak(string $replacement = ''): self
3370
    {
3371
        return static::create(
13✔
3372
            $this->utf8::remove_html_breaks($this->str, $replacement),
13✔
3373
            $this->encoding
13✔
3374
        );
13✔
3375
    }
3376

3377
    /**
3378
     * Returns a new string with the prefix $substring removed, if present.
3379
     *
3380
     * EXAMPLE: <code>
3381
     * s('fòôbàř')->removeLeft('fòô'); // 'bàř'
3382
     * </code>
3383
     *
3384
     * @param string $substring <p>The prefix to remove.</p>
3385
     *
3386
     * @psalm-mutation-free
3387
     *
3388
     * @return static
3389
     *                <p>Object having a $str without the prefix $substring.</p>
3390
     */
3391
    public function removeLeft(string $substring): self
3392
    {
3393
        return static::create(
36✔
3394
            $this->utf8::remove_left($this->str, $substring, $this->encoding),
36✔
3395
            $this->encoding
36✔
3396
        );
36✔
3397
    }
3398

3399
    /**
3400
     * Returns a new string with the suffix $substring removed, if present.
3401
     *
3402
     * EXAMPLE: <code>
3403
     * s('fòôbàř')->removeRight('bàř'); // 'fòô'
3404
     * </code>
3405
     *
3406
     * @param string $substring <p>The suffix to remove.</p>
3407
     *
3408
     * @psalm-mutation-free
3409
     *
3410
     * @return static
3411
     *                <p>Object having a $str without the suffix $substring.</p>
3412
     */
3413
    public function removeRight(string $substring): self
3414
    {
3415
        return static::create(
36✔
3416
            $this->utf8::remove_right($this->str, $substring, $this->encoding),
36✔
3417
            $this->encoding
36✔
3418
        );
36✔
3419
    }
3420

3421
    /**
3422
     * Try to remove all XSS-attacks from the string.
3423
     *
3424
     * EXAMPLE: <code>
3425
     * s('<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>')->removeXss(); // '<IMG >'
3426
     * </code>
3427
     *
3428
     * @psalm-mutation-free
3429
     *
3430
     * @return static
3431
     */
3432
    public function removeXss(): self
3433
    {
3434
        /**
3435
         * @var AntiXSS|null
3436
         *
3437
         * @psalm-suppress ImpureStaticVariable
3438
         */
3439
        static $antiXss = null;
12✔
3440

3441
        if ($antiXss === null) {
12✔
3442
            $antiXss = new AntiXSS();
1✔
3443
        }
3444

3445
        /**
3446
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to the anti-xss class
3447
         */
3448
        $str = $antiXss->xss_clean($this->str);
12✔
3449

3450
        return static::create($str, $this->encoding);
12✔
3451
    }
3452

3453
    /**
3454
     * Returns a repeated string given a multiplier.
3455
     *
3456
     * EXAMPLE: <code>
3457
     * s('α')->repeat(3); // 'ααα'
3458
     * </code>
3459
     *
3460
     * @param int $multiplier <p>The number of times to repeat the string.</p>
3461
     *
3462
     * @psalm-mutation-free
3463
     *
3464
     * @return static
3465
     *                <p>Object with a repeated str.</p>
3466
     */
3467
    public function repeat(int $multiplier): self
3468
    {
3469
        return static::create(
21✔
3470
            \str_repeat($this->str, $multiplier),
21✔
3471
            $this->encoding
21✔
3472
        );
21✔
3473
    }
3474

3475
    /**
3476
     * Replaces all occurrences of $search in $str by $replacement.
3477
     *
3478
     * EXAMPLE: <code>
3479
     * s('fòô bàř fòô bàř')->replace('fòô ', ''); // 'bàř bàř'
3480
     * </code>
3481
     *
3482
     * @param string $search        <p>The needle to search for.</p>
3483
     * @param string $replacement   <p>The string to replace with.</p>
3484
     * @param bool   $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
3485
     *
3486
     * @psalm-mutation-free
3487
     *
3488
     * @return static
3489
     *                <p>Object with the resulting $str after the replacements.</p>
3490
     */
3491
    public function replace(string $search, string $replacement, bool $caseSensitive = true): self
3492
    {
3493
        if ($search === '' && $replacement === '') {
76✔
3494
            return static::create($this->str, $this->encoding);
16✔
3495
        }
3496

3497
        if ($this->str === '' && $search === '') {
60✔
3498
            return static::create($replacement, $this->encoding);
2✔
3499
        }
3500

3501
        if ($caseSensitive) {
58✔
3502
            return static::create(
48✔
3503
                \str_replace($search, $replacement, $this->str),
48✔
3504
                $this->encoding
48✔
3505
            );
48✔
3506
        }
3507

3508
        return static::create(
10✔
3509
            $this->utf8::str_ireplace($search, $replacement, $this->str),
10✔
3510
            $this->encoding
10✔
3511
        );
10✔
3512
    }
3513

3514
    /**
3515
     * Replaces all occurrences of $search in $str by $replacement.
3516
     *
3517
     * EXAMPLE: <code>
3518
     * s('fòô bàř lall bàř')->replaceAll(['fòÔ ', 'lall'], '', false); // 'bàř bàř'
3519
     * </code>
3520
     *
3521
     * @param string[]        $search        <p>The elements to search for.</p>
3522
     * @param string|string[] $replacement   <p>The string to replace with.</p>
3523
     * @param bool            $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
3524
     *
3525
     * @psalm-mutation-free
3526
     *
3527
     * @return static
3528
     *                <p>Object with the resulting $str after the replacements.</p>
3529
     */
3530
    public function replaceAll(array $search, $replacement, bool $caseSensitive = true): self
3531
    {
3532
        if ($caseSensitive) {
61✔
3533
            return static::create(
47✔
3534
                \str_replace($search, $replacement, $this->str),
47✔
3535
                $this->encoding
47✔
3536
            );
47✔
3537
        }
3538

3539
        return static::create(
14✔
3540
            $this->utf8::str_ireplace($search, $replacement, $this->str),
14✔
3541
            $this->encoding
14✔
3542
        );
14✔
3543
    }
3544

3545
    /**
3546
     * Replaces all occurrences of $search from the beginning of string with $replacement.
3547
     *
3548
     * EXAMPLE: <code>
3549
     * s('fòô bàř fòô bàř')->replaceBeginning('fòô', ''); // ' bàř bàř'
3550
     * </code>
3551
     *
3552
     * @param string $search      <p>The string to search for.</p>
3553
     * @param string $replacement <p>The replacement.</p>
3554
     *
3555
     * @psalm-mutation-free
3556
     *
3557
     * @return static
3558
     *                <p>Object with the resulting $str after the replacements.</p>
3559
     */
3560
    public function replaceBeginning(string $search, string $replacement): self
3561
    {
3562
        return static::create(
32✔
3563
            $this->utf8::str_replace_beginning($this->str, $search, $replacement),
32✔
3564
            $this->encoding
32✔
3565
        );
32✔
3566
    }
3567

3568
    /**
3569
     * Replaces all occurrences of $search from the ending of string with $replacement.
3570
     *
3571
     * EXAMPLE: <code>
3572
     * s('fòô bàř fòô bàř')->replaceEnding('bàř', ''); // 'fòô bàř fòô '
3573
     * </code>
3574
     *
3575
     * @param string $search      <p>The string to search for.</p>
3576
     * @param string $replacement <p>The replacement.</p>
3577
     *
3578
     * @psalm-mutation-free
3579
     *
3580
     * @return static
3581
     *                <p>Object with the resulting $str after the replacements.</p>
3582
     */
3583
    public function replaceEnding(string $search, string $replacement): self
3584
    {
3585
        return static::create(
32✔
3586
            $this->utf8::str_replace_ending($this->str, $search, $replacement),
32✔
3587
            $this->encoding
32✔
3588
        );
32✔
3589
    }
3590

3591
    /**
3592
     * Replaces first occurrences of $search from the beginning of string with $replacement.
3593
     *
3594
     * EXAMPLE: <code>
3595
     * </code>
3596
     *
3597
     * @param string $search      <p>The string to search for.</p>
3598
     * @param string $replacement <p>The replacement.</p>
3599
     *
3600
     * @psalm-mutation-free
3601
     *
3602
     * @return static
3603
     *                <p>Object with the resulting $str after the replacements.</p>
3604
     */
3605
    public function replaceFirst(string $search, string $replacement): self
3606
    {
3607
        return static::create(
32✔
3608
            $this->utf8::str_replace_first($search, $replacement, $this->str),
32✔
3609
            $this->encoding
32✔
3610
        );
32✔
3611
    }
3612

3613
    /**
3614
     * Replaces last occurrences of $search from the ending of string with $replacement.
3615
     *
3616
     * EXAMPLE: <code>
3617
     * </code>
3618
     *
3619
     * @param string $search      <p>The string to search for.</p>
3620
     * @param string $replacement <p>The replacement.</p>
3621
     *
3622
     * @psalm-mutation-free
3623
     *
3624
     * @return static
3625
     *                <p>Object with the resulting $str after the replacements.</p>
3626
     */
3627
    public function replaceLast(string $search, string $replacement): self
3628
    {
3629
        return static::create(
30✔
3630
            $this->utf8::str_replace_last($search, $replacement, $this->str),
30✔
3631
            $this->encoding
30✔
3632
        );
30✔
3633
    }
3634

3635
    /**
3636
     * Returns a reversed string. A multibyte version of strrev().
3637
     *
3638
     * EXAMPLE: <code>
3639
     * s('fòôbàř')->reverse(); // 'řàbôòf'
3640
     * </code>
3641
     *
3642
     * @psalm-mutation-free
3643
     *
3644
     * @return static
3645
     *                <p>Object with a reversed $str.</p>
3646
     */
3647
    public function reverse(): self
3648
    {
3649
        return static::create($this->utf8::strrev($this->str), $this->encoding);
15✔
3650
    }
3651

3652
    /**
3653
     * Truncates the string to a given length, while ensuring that it does not
3654
     * split words. If $substring is provided, and truncating occurs, the
3655
     * string is further truncated so that the substring may be appended without
3656
     * exceeding the desired length.
3657
     *
3658
     * EXAMPLE: <code>
3659
     * s('What are your plans today?')->safeTruncate(22, '...'); // 'What are your plans...'
3660
     * </code>
3661
     *
3662
     * @param int    $length                          <p>Desired length of the truncated string.</p>
3663
     * @param string $substring                       [optional] <p>The substring to append if it can fit. Default: ''</p>
3664
     * @param bool   $ignoreDoNotSplitWordsForOneWord
3665
     *
3666
     * @psalm-mutation-free
3667
     *
3668
     * @return static
3669
     *                <p>Object with the resulting $str after truncating.</p>
3670
     */
3671
    public function safeTruncate(
3672
        int $length,
3673
        string $substring = '',
3674
        bool $ignoreDoNotSplitWordsForOneWord = true
3675
    ): self {
3676
        return static::create(
68✔
3677
            $this->utf8::str_truncate_safe(
68✔
3678
                $this->str,
68✔
3679
                $length,
68✔
3680
                $substring,
68✔
3681
                $this->encoding,
68✔
3682
                $ignoreDoNotSplitWordsForOneWord
68✔
3683
            ),
68✔
3684
            $this->encoding
68✔
3685
        );
68✔
3686
    }
3687

3688
    /**
3689
     * Set the internal character encoding.
3690
     *
3691
     * EXAMPLE: <code>
3692
     * </code>
3693
     *
3694
     * @param string $new_encoding <p>The desired character encoding.</p>
3695
     *
3696
     * @psalm-mutation-free
3697
     *
3698
     * @return static
3699
     */
3700
    public function setInternalEncoding(string $new_encoding): self
3701
    {
3702
        return new static($this->str, $new_encoding);
1✔
3703
    }
3704

3705
    /**
3706
     * Create a sha1 hash from the current string.
3707
     *
3708
     * EXAMPLE: <code>
3709
     * </code>
3710
     *
3711
     * @psalm-mutation-free
3712
     *
3713
     * @return static
3714
     */
3715
    public function sha1(): self
3716
    {
3717
        return static::create($this->hash('sha1'), $this->encoding);
2✔
3718
    }
3719

3720
    /**
3721
     * Create a sha256 hash from the current string.
3722
     *
3723
     * EXAMPLE: <code>
3724
     * </code>
3725
     *
3726
     * @psalm-mutation-free
3727
     *
3728
     * @return static
3729
     */
3730
    public function sha256(): self
3731
    {
3732
        return static::create($this->hash('sha256'), $this->encoding);
2✔
3733
    }
3734

3735
    /**
3736
     * Create a sha512 hash from the current string.
3737
     *
3738
     * EXAMPLE: <code>
3739
     * </code>
3740
     *
3741
     * @psalm-mutation-free
3742
     *
3743
     * @return static
3744
     */
3745
    public function sha512(): self
3746
    {
3747
        return static::create($this->hash('sha512'), $this->encoding);
2✔
3748
    }
3749

3750
    /**
3751
     * Shorten the string after $length, but also after the next word.
3752
     *
3753
     * EXAMPLE: <code>
3754
     * s('this is a test')->shortenAfterWord(2, '...'); // 'this...'
3755
     * </code>
3756
     *
3757
     * @param int    $length   <p>The given length.</p>
3758
     * @param string $strAddOn [optional] <p>Default: '…'</p>
3759
     *
3760
     * @psalm-mutation-free
3761
     *
3762
     * @return static
3763
     */
3764
    public function shortenAfterWord(int $length, string $strAddOn = '…'): self
3765
    {
3766
        return static::create(
8✔
3767
            $this->utf8::str_limit_after_word($this->str, $length, $strAddOn),
8✔
3768
            $this->encoding
8✔
3769
        );
8✔
3770
    }
3771

3772
    /**
3773
     * A multibyte string shuffle function. It returns a string with its
3774
     * characters in random order.
3775
     *
3776
     * EXAMPLE: <code>
3777
     * s('fòôbàř')->shuffle(); // 'àôřbòf'
3778
     * </code>
3779
     *
3780
     * @return static
3781
     *                <p>Object with a shuffled $str.</p>
3782
     */
3783
    public function shuffle(): self
3784
    {
3785
        return static::create($this->utf8::str_shuffle($this->str), $this->encoding);
9✔
3786
    }
3787

3788
    /**
3789
     * Calculate the similarity between two strings.
3790
     *
3791
     * EXAMPLE: <code>
3792
     * </code>
3793
     *
3794
     * @param string $str <p>The delimiting string.</p>
3795
     *
3796
     * @psalm-mutation-free
3797
     *
3798
     * @return float
3799
     */
3800
    public function similarity(string $str): float
3801
    {
3802
        \similar_text($this->str, $str, $percent);
2✔
3803

3804
        return $percent;
2✔
3805
    }
3806

3807
    /**
3808
     * Returns the substring beginning at $start, and up to, but not including
3809
     * the index specified by $end. If $end is omitted, the function extracts
3810
     * the remaining string. If $end is negative, it is computed from the end
3811
     * of the string.
3812
     *
3813
     * EXAMPLE: <code>
3814
     * s('fòôbàř')->slice(3, -1); // 'bà'
3815
     * </code>
3816
     *
3817
     * @param int $start <p>Initial index from which to begin extraction.</p>
3818
     * @param int $end   [optional] <p>Index at which to end extraction. Default: null</p>
3819
     *
3820
     * @psalm-mutation-free
3821
     *
3822
     * @return static
3823
     *                <p>Object with its $str being the extracted substring.</p>
3824
     */
3825
    public function slice(int $start, int $end = null): self
3826
    {
3827
        return static::create(
50✔
3828
            $this->utf8::str_slice($this->str, $start, $end, $this->encoding),
50✔
3829
            $this->encoding
50✔
3830
        );
50✔
3831
    }
3832

3833
    /**
3834
     * Converts the string into an URL slug. This includes replacing non-ASCII
3835
     * characters with their closest ASCII equivalents, removing remaining
3836
     * non-ASCII and non-alphanumeric characters, and replacing whitespace with
3837
     * $separator. The separator defaults to a single dash, and the string
3838
     * is also converted to lowercase. The language of the source string can
3839
     * also be supplied for language-specific transliteration.
3840
     *
3841
     * EXAMPLE: <code>
3842
     * s('Using strings like fòô bàř')->slugify(); // 'using-strings-like-foo-bar'
3843
     * </code>
3844
     *
3845
     * @param string                $separator             [optional] <p>The string used to replace whitespace.</p>
3846
     * @param string                $language              [optional] <p>Language of the source string.</p>
3847
     * @param array<string, string> $replacements          [optional] <p>A map of replaceable strings.</p>
3848
     * @param bool                  $replace_extra_symbols [optional]  <p>Add some more replacements e.g. "£" with "
3849
     *                                                     pound ".</p>
3850
     * @param bool                  $use_str_to_lower      [optional] <p>Use "string to lower" for the input.</p>
3851
     * @param bool                  $use_transliterate     [optional]  <p>Use ASCII::to_transliterate() for unknown
3852
     *                                                     chars.</p>
3853
     *
3854
     * @psalm-mutation-free
3855
     *
3856
     * @return static
3857
     *                <p>Object whose $str has been converted to an URL slug.</p>
3858
     *
3859
     * @phpstan-param ASCII::*_LANGUAGE_CODE $language
3860
     *
3861
     * @noinspection PhpTooManyParametersInspection
3862
     */
3863
    public function slugify(
3864
        string $separator = '-',
3865
        string $language = 'en',
3866
        array $replacements = [],
3867
        bool $replace_extra_symbols = true,
3868
        bool $use_str_to_lower = true,
3869
        bool $use_transliterate = false
3870
    ): self {
3871
        return static::create(
17✔
3872
            $this->ascii::to_slugify(
17✔
3873
                $this->str,
17✔
3874
                $separator,
17✔
3875
                $language,
17✔
3876
                $replacements,
17✔
3877
                $replace_extra_symbols,
17✔
3878
                $use_str_to_lower,
17✔
3879
                $use_transliterate
17✔
3880
            ),
17✔
3881
            $this->encoding
17✔
3882
        );
17✔
3883
    }
3884

3885
    /**
3886
     * Convert the string to snake_case.
3887
     *
3888
     * EXAMPLE: <code>
3889
     * </code>
3890
     *
3891
     * @psalm-mutation-free
3892
     *
3893
     * @return static
3894
     */
3895
    public function snakeCase(): self
3896
    {
3897
        $words = \array_map(
3✔
3898
            static function (self $word) {
3✔
3899
                return $word->toLowerCase();
3✔
3900
            },
3✔
3901
            $this->words('', true)
3✔
3902
        );
3✔
3903

3904
        return new static(\implode('_', $words), $this->encoding);
3✔
3905
    }
3906

3907
    /**
3908
     * Convert a string to snake_case.
3909
     *
3910
     * EXAMPLE: <code>
3911
     * s('foo1 Bar')->snakeize(); // 'foo_1_bar'
3912
     * </code>
3913
     *
3914
     * @psalm-mutation-free
3915
     *
3916
     * @return static
3917
     *                <p>Object with $str in snake_case.</p>
3918
     */
3919
    public function snakeize(): self
3920
    {
3921
        return static::create(
40✔
3922
            $this->utf8::str_snakeize($this->str, $this->encoding),
40✔
3923
            $this->encoding
40✔
3924
        );
40✔
3925
    }
3926

3927
    /**
3928
     * Wrap the string after the first whitespace character after a given number
3929
     * of characters.
3930
     *
3931
     * EXAMPLE: <code>
3932
     * </code>
3933
     *
3934
     * @param int    $width <p>Number of characters at which to wrap.</p>
3935
     * @param string $break [optional] <p>Character used to break the string. | Default "\n"</p>
3936
     *
3937
     * @psalm-mutation-free
3938
     *
3939
     * @return static
3940
     */
3941
    public function softWrap(int $width, string $break = "\n"): self
3942
    {
3943
        return $this->lineWrapAfterWord($width, $break, false);
2✔
3944
    }
3945

3946
    /**
3947
     * Splits the string with the provided regular expression, returning an
3948
     * array of Stringy objects. An optional integer $limit will truncate the
3949
     * results.
3950
     *
3951
     * EXAMPLE: <code>
3952
     * s('foo,bar,baz')->split(',', 2); // ['foo', 'bar']
3953
     * </code>
3954
     *
3955
     * @param string $pattern <p>The regex with which to split the string.</p>
3956
     * @param int    $limit   [optional] <p>Maximum number of results to return. Default: -1 === no
3957
     *                        limit</p>
3958
     *
3959
     * @psalm-mutation-free
3960
     *
3961
     * @return static[]
3962
     *                  <p>An array of Stringy objects.</p>
3963
     *
3964
     * @phpstan-return array<int,static>
3965
     */
3966
    public function split(string $pattern, int $limit = null): array
3967
    {
3968
        if ($this->str === '') {
51✔
3969
            return [];
×
3970
        }
3971

3972
        if ($limit === null) {
51✔
3973
            $limit = -1;
7✔
3974
        }
3975

3976
        $array = $this->utf8::str_split_pattern($this->str, $pattern, $limit);
51✔
3977
        foreach ($array as &$value) {
51✔
3978
            $value = static::create($value, $this->encoding);
45✔
3979
        }
3980

3981
        /** @noinspection PhpSillyAssignmentInspection */
3982
        /** @var static[] $array */
3983
        $array = $array;
51✔
3984

3985
        return $array;
51✔
3986
    }
3987

3988
    /**
3989
     * Splits the string with the provided regular expression, returning an
3990
     * collection of Stringy objects. An optional integer $limit will truncate the
3991
     * results.
3992
     *
3993
     * EXAMPLE: <code>
3994
     * </code>
3995
     *
3996
     * @param string $pattern <p>The regex with which to split the string.</p>
3997
     * @param int    $limit   [optional] <p>Maximum number of results to return. Default: -1 === no
3998
     *                        limit</p>
3999
     *
4000
     * @psalm-mutation-free
4001
     *
4002
     * @return CollectionStringy|static[]
4003
     *                                    <p>An collection of Stringy objects.</p>
4004
     *
4005
     * @phpstan-return CollectionStringy<int,static>
4006
     */
4007
    public function splitCollection(string $pattern, int $limit = null): CollectionStringy
4008
    {
4009
        /**
4010
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to the collection class
4011
         */
4012
        return CollectionStringy::create(
35✔
4013
            $this->split($pattern, $limit)
35✔
4014
        );
35✔
4015
    }
4016

4017
    /**
4018
     * Returns true if the string begins with $substring, false otherwise. By
4019
     * default, the comparison is case-sensitive, but can be made insensitive
4020
     * by setting $caseSensitive to false.
4021
     *
4022
     * EXAMPLE: <code>
4023
     * s('FÒÔbàřbaz')->startsWith('fòôbàř', false); // true
4024
     * </code>
4025
     *
4026
     * @param string $substring     <p>The substring to look for.</p>
4027
     * @param bool   $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
4028
     *
4029
     * @psalm-mutation-free
4030
     *
4031
     * @return bool
4032
     *              <p>Whether or not $str starts with $substring.</p>
4033
     */
4034
    public function startsWith(string $substring, bool $caseSensitive = true): bool
4035
    {
4036
        if ($caseSensitive) {
99✔
4037
            return $this->utf8::str_starts_with($this->str, $substring);
53✔
4038
        }
4039

4040
        return $this->utf8::str_istarts_with($this->str, $substring);
46✔
4041
    }
4042

4043
    /**
4044
     * Returns true if the string begins with any of $substrings, false otherwise.
4045
     * By default the comparison is case-sensitive, but can be made insensitive by
4046
     * setting $caseSensitive to false.
4047
     *
4048
     * EXAMPLE: <code>
4049
     * s('FÒÔbàřbaz')->startsWithAny(['fòô', 'bàř'], false); // true
4050
     * </code>
4051
     *
4052
     * @param string[] $substrings    <p>Substrings to look for.</p>
4053
     * @param bool     $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
4054
     *
4055
     * @psalm-mutation-free
4056
     *
4057
     * @return bool
4058
     *              <p>Whether or not $str starts with $substring.</p>
4059
     */
4060
    public function startsWithAny(array $substrings, bool $caseSensitive = true): bool
4061
    {
4062
        if ($caseSensitive) {
35✔
4063
            return $this->utf8::str_starts_with_any($this->str, $substrings);
23✔
4064
        }
4065

4066
        return $this->utf8::str_istarts_with_any($this->str, $substrings);
12✔
4067
    }
4068

4069
    /**
4070
     * Remove one or more strings from the string.
4071
     *
4072
     * EXAMPLE: <code>
4073
     * </code>
4074
     *
4075
     * @param string|string[] $search One or more strings to be removed
4076
     *
4077
     * @psalm-mutation-free
4078
     *
4079
     * @return static
4080
     */
4081
    public function strip($search): self
4082
    {
4083
        if (\is_array($search)) {
3✔
4084
            return $this->replaceAll($search, '');
1✔
4085
        }
4086

4087
        return $this->replace($search, '');
2✔
4088
    }
4089

4090
    /**
4091
     * Strip all whitespace characters. This includes tabs and newline characters,
4092
     * as well as multibyte whitespace such as the thin space and ideographic space.
4093
     *
4094
     * EXAMPLE: <code>
4095
     * s('   Ο     συγγραφέας  ')->stripWhitespace(); // 'Οσυγγραφέας'
4096
     * </code>
4097
     *
4098
     * @psalm-mutation-free
4099
     *
4100
     * @return static
4101
     */
4102
    public function stripWhitespace(): self
4103
    {
4104
        return static::create(
36✔
4105
            $this->utf8::strip_whitespace($this->str),
36✔
4106
            $this->encoding
36✔
4107
        );
36✔
4108
    }
4109

4110
    /**
4111
     * Remove css media-queries.
4112
     *
4113
     * EXAMPLE: <code>
4114
     * s('test @media (min-width:660px){ .des-cla #mv-tiles{width:480px} } test ')->stripeCssMediaQueries(); // 'test  test '
4115
     * </code>
4116
     *
4117
     * @psalm-mutation-free
4118
     *
4119
     * @return static
4120
     */
4121
    public function stripeCssMediaQueries(): self
4122
    {
4123
        return static::create(
2✔
4124
            $this->utf8::css_stripe_media_queries($this->str),
2✔
4125
            $this->encoding
2✔
4126
        );
2✔
4127
    }
4128

4129
    /**
4130
     * Remove empty html-tag.
4131
     *
4132
     * EXAMPLE: <code>
4133
     * s('foo<h1></h1>bar')->stripeEmptyHtmlTags(); // 'foobar'
4134
     * </code>
4135
     *
4136
     * @psalm-mutation-free
4137
     *
4138
     * @return static
4139
     */
4140
    public function stripeEmptyHtmlTags(): self
4141
    {
4142
        return static::create(
2✔
4143
            $this->utf8::html_stripe_empty_tags($this->str),
2✔
4144
            $this->encoding
2✔
4145
        );
2✔
4146
    }
4147

4148
    /**
4149
     * Convert the string to StudlyCase.
4150
     *
4151
     * EXAMPLE: <code>
4152
     * </code>
4153
     *
4154
     * @psalm-mutation-free
4155
     *
4156
     * @return static
4157
     */
4158
    public function studlyCase(): self
4159
    {
4160
        $words = \array_map(
6✔
4161
            static function (self $word) {
6✔
4162
                return $word->substr(0, 1)
6✔
4163
                    ->toUpperCase()
6✔
4164
                    ->appendStringy($word->substr(1));
6✔
4165
            },
6✔
4166
            $this->words('', true)
6✔
4167
        );
6✔
4168

4169
        return new static(\implode('', $words), $this->encoding);
6✔
4170
    }
4171

4172
    /**
4173
     * Returns the substring beginning at $start with the specified $length.
4174
     * It differs from the $this->utf8::substr() function in that providing a $length of
4175
     * null will return the rest of the string, rather than an empty string.
4176
     *
4177
     * EXAMPLE: <code>
4178
     * </code>
4179
     *
4180
     * @param int $start  <p>Position of the first character to use.</p>
4181
     * @param int $length [optional] <p>Maximum number of characters used. Default: null</p>
4182
     *
4183
     * @psalm-mutation-free
4184
     *
4185
     * @return static
4186
     *                <p>Object with its $str being the substring.</p>
4187
     */
4188
    public function substr(int $start, int $length = null): self
4189
    {
4190
        return static::create(
40✔
4191
            $this->utf8::substr(
40✔
4192
                $this->str,
40✔
4193
                $start,
40✔
4194
                $length,
40✔
4195
                $this->encoding
40✔
4196
            ),
40✔
4197
            $this->encoding
40✔
4198
        );
40✔
4199
    }
4200

4201
    /**
4202
     * Return part of the string.
4203
     * Alias for substr()
4204
     *
4205
     * EXAMPLE: <code>
4206
     * s('fòôbàř')->substring(2, 3); // 'ôbà'
4207
     * </code>
4208
     *
4209
     * @param int $start  <p>Starting position of the substring.</p>
4210
     * @param int $length [optional] <p>Length of substring.</p>
4211
     *
4212
     * @psalm-mutation-free
4213
     *
4214
     * @return static
4215
     */
4216
    public function substring(int $start, int $length = null): self
4217
    {
4218
        if ($length === null) {
3✔
4219
            return $this->substr($start);
2✔
4220
        }
4221

4222
        return $this->substr($start, $length);
3✔
4223
    }
4224

4225
    /**
4226
     * Gets the substring after (or before via "$beforeNeedle") the first occurrence of the "$needle".
4227
     * If no match is found returns new empty Stringy object.
4228
     *
4229
     * EXAMPLE: <code>
4230
     * </code>
4231
     *
4232
     * @param string $needle       <p>The string to look for.</p>
4233
     * @param bool   $beforeNeedle [optional] <p>Default: false</p>
4234
     *
4235
     * @psalm-mutation-free
4236
     *
4237
     * @return static
4238
     */
4239
    public function substringOf(string $needle, bool $beforeNeedle = false): self
4240
    {
4241
        return static::create(
4✔
4242
            $this->utf8::str_substr_first(
4✔
4243
                $this->str,
4✔
4244
                $needle,
4✔
4245
                $beforeNeedle,
4✔
4246
                $this->encoding
4✔
4247
            ),
4✔
4248
            $this->encoding
4✔
4249
        );
4✔
4250
    }
4251

4252
    /**
4253
     * Gets the substring after (or before via "$beforeNeedle") the first occurrence of the "$needle".
4254
     * If no match is found returns new empty Stringy object.
4255
     *
4256
     * EXAMPLE: <code>
4257
     * </code>
4258
     *
4259
     * @param string $needle       <p>The string to look for.</p>
4260
     * @param bool   $beforeNeedle [optional] <p>Default: false</p>
4261
     *
4262
     * @psalm-mutation-free
4263
     *
4264
     * @return static
4265
     */
4266
    public function substringOfIgnoreCase(string $needle, bool $beforeNeedle = false): self
4267
    {
4268
        return static::create(
4✔
4269
            $this->utf8::str_isubstr_first(
4✔
4270
                $this->str,
4✔
4271
                $needle,
4✔
4272
                $beforeNeedle,
4✔
4273
                $this->encoding
4✔
4274
            ),
4✔
4275
            $this->encoding
4✔
4276
        );
4✔
4277
    }
4278

4279
    /**
4280
     * Surrounds $str with the given substring.
4281
     *
4282
     * EXAMPLE: <code>
4283
     * s(' ͜ ')->surround('ʘ'); // 'ʘ ͜ ʘ'
4284
     * </code>
4285
     *
4286
     * @param string $substring <p>The substring to add to both sides.</P>
4287
     *
4288
     * @psalm-mutation-free
4289
     *
4290
     * @return static
4291
     *                <p>Object whose $str had the substring both prepended and appended.</p>
4292
     */
4293
    public function surround(string $substring): self
4294
    {
4295
        return static::create(
15✔
4296
            $substring . $this->str . $substring,
15✔
4297
            $this->encoding
15✔
4298
        );
15✔
4299
    }
4300

4301
    /**
4302
     * Returns a case swapped version of the string.
4303
     *
4304
     * EXAMPLE: <code>
4305
     * s('Ντανιλ')->swapCase(); // 'νΤΑΝΙΛ'
4306
     * </code>
4307
     *
4308
     * @psalm-mutation-free
4309
     *
4310
     * @return static
4311
     *                <p>Object whose $str has each character's case swapped.</P>
4312
     */
4313
    public function swapCase(): self
4314
    {
4315
        return static::create(
15✔
4316
            $this->utf8::swapCase($this->str, $this->encoding),
15✔
4317
            $this->encoding
15✔
4318
        );
15✔
4319
    }
4320

4321
    /**
4322
     * Returns a string with smart quotes, ellipsis characters, and dashes from
4323
     * Windows-1252 (commonly used in Word documents) replaced by their ASCII
4324
     * equivalents.
4325
     *
4326
     * EXAMPLE: <code>
4327
     * s('“I see…”')->tidy(); // '"I see..."'
4328
     * </code>
4329
     *
4330
     * @psalm-mutation-free
4331
     *
4332
     * @return static
4333
     *                <p>Object whose $str has those characters removed.</p>
4334
     */
4335
    public function tidy(): self
4336
    {
4337
        return static::create(
12✔
4338
            $this->ascii::normalize_msword($this->str),
12✔
4339
            $this->encoding
12✔
4340
        );
12✔
4341
    }
4342

4343
    /**
4344
     * Returns a trimmed string with the first letter of each word capitalized.
4345
     * Also accepts an array, $ignore, allowing you to list words not to be
4346
     * capitalized.
4347
     *
4348
     * EXAMPLE: <code>
4349
     * $ignore = ['at', 'by', 'for', 'in', 'of', 'on', 'out', 'to', 'the'];
4350
     * s('i like to watch television')->titleize($ignore); // 'I Like to Watch Television'
4351
     * </code>
4352
     *
4353
     * @param string[]|null $ignore            [optional] <p>An array of words not to capitalize or null.
4354
     *                                         Default: null</p>
4355
     * @param string|null   $word_define_chars [optional] <p>An string of chars that will be used as whitespace
4356
     *                                         separator === words.</p>
4357
     * @param string|null   $language          [optional] <p>Language of the source string.</p>
4358
     *
4359
     * @psalm-mutation-free
4360
     *
4361
     * @return static
4362
     *                <p>Object with a titleized $str.</p>
4363
     */
4364
    public function titleize(
4365
        array $ignore = null,
4366
        string $word_define_chars = null,
4367
        string $language = null
4368
    ): self {
4369
        return static::create(
23✔
4370
            $this->utf8::str_titleize(
23✔
4371
                $this->str,
23✔
4372
                $ignore,
23✔
4373
                $this->encoding,
23✔
4374
                false,
23✔
4375
                $language,
23✔
4376
                false,
23✔
4377
                true,
23✔
4378
                $word_define_chars
23✔
4379
            ),
23✔
4380
            $this->encoding
23✔
4381
        );
23✔
4382
    }
4383

4384
    /**
4385
     * Returns a trimmed string in proper title case: Also accepts an array, $ignore, allowing you to list words not to
4386
     * be capitalized.
4387
     *
4388
     * EXAMPLE: <code>
4389
     * </code>
4390
     *
4391
     * Adapted from John Gruber's script.
4392
     *
4393
     * @see https://gist.github.com/gruber/9f9e8650d68b13ce4d78
4394
     *
4395
     * @param string[] $ignore <p>An array of words not to capitalize.</p>
4396
     *
4397
     * @psalm-mutation-free
4398
     *
4399
     * @return static
4400
     *                <p>Object with a titleized $str</p>
4401
     */
4402
    public function titleizeForHumans(array $ignore = []): self
4403
    {
4404
        return static::create(
70✔
4405
            $this->utf8::str_titleize_for_humans(
70✔
4406
                $this->str,
70✔
4407
                $ignore,
70✔
4408
                $this->encoding
70✔
4409
            ),
70✔
4410
            $this->encoding
70✔
4411
        );
70✔
4412
    }
4413

4414
    /**
4415
     * Returns an ASCII version of the string. A set of non-ASCII characters are
4416
     * replaced with their closest ASCII counterparts, and the rest are removed
4417
     * by default. The language or locale of the source string can be supplied
4418
     * for language-specific transliteration in any of the following formats:
4419
     * en, en_GB, or en-GB. For example, passing "de" results in "äöü" mapping
4420
     * to "aeoeue" rather than "aou" as in other languages.
4421
     *
4422
     * EXAMPLE: <code>
4423
     * s('fòôbàř')->toAscii(); // 'foobar'
4424
     * </code>
4425
     *
4426
     * @param string $language          [optional] <p>Language of the source string.</p>
4427
     * @param bool   $removeUnsupported [optional] <p>Whether or not to remove the
4428
     *                                  unsupported characters.</p>
4429
     *
4430
     * @psalm-mutation-free
4431
     *
4432
     * @return static
4433
     *                <p>Object whose $str contains only ASCII characters.</p>
4434
     *
4435
     * @phpstan-param ASCII::*_LANGUAGE_CODE $language
4436
     */
4437
    public function toAscii(string $language = 'en', bool $removeUnsupported = true): self
4438
    {
4439
        return static::create(
23✔
4440
            $this->ascii::to_ascii(
23✔
4441
                $this->str,
23✔
4442
                $language,
23✔
4443
                $removeUnsupported
23✔
4444
            ),
23✔
4445
            $this->encoding
23✔
4446
        );
23✔
4447
    }
4448

4449
    /**
4450
     * Returns a boolean representation of the given logical string value.
4451
     * For example, <strong>'true', '1', 'on' and 'yes'</strong> will return true. <strong>'false', '0',
4452
     * 'off', and 'no'</strong> will return false. In all instances, case is ignored.
4453
     * For other numeric strings, their sign will determine the return value.
4454
     * In addition, blank strings consisting of only whitespace will return
4455
     * false. For all other strings, the return value is a result of a
4456
     * boolean cast.
4457
     *
4458
     * EXAMPLE: <code>
4459
     * s('OFF')->toBoolean(); // false
4460
     * </code>
4461
     *
4462
     * @psalm-mutation-free
4463
     *
4464
     * @return bool
4465
     *              <p>A boolean value for the string.</p>
4466
     */
4467
    public function toBoolean(): bool
4468
    {
4469
        /**
4470
         * @psalm-suppress ArgumentTypeCoercion -> maybe the string looks like an int ;)
4471
         * @phpstan-ignore-next-line
4472
         */
4473
        return $this->utf8::to_boolean($this->str);
45✔
4474
    }
4475

4476
    /**
4477
     * Converts all characters in the string to lowercase.
4478
     *
4479
     * EXAMPLE: <code>
4480
     * s('FÒÔBÀŘ')->toLowerCase(); // 'fòôbàř'
4481
     * </code>
4482
     *
4483
     * @param bool        $tryToKeepStringLength [optional] <p>true === try to keep the string length: e.g. ẞ -> ß</p>
4484
     * @param string|null $lang                  [optional] <p>Set the language for special cases: az, el, lt, tr</p>
4485
     *
4486
     * @psalm-mutation-free
4487
     *
4488
     * @return static
4489
     *                <p>Object with all characters of $str being lowercase.</p>
4490
     */
4491
    public function toLowerCase($tryToKeepStringLength = false, $lang = null): self
4492
    {
4493
        return static::create(
23✔
4494
            $this->utf8::strtolower(
23✔
4495
                $this->str,
23✔
4496
                $this->encoding,
23✔
4497
                false,
23✔
4498
                $lang,
23✔
4499
                $tryToKeepStringLength
23✔
4500
            ),
23✔
4501
            $this->encoding
23✔
4502
        );
23✔
4503
    }
4504

4505
    /**
4506
     * Converts each tab in the string to some number of spaces, as defined by
4507
     * $tabLength. By default, each tab is converted to 4 consecutive spaces.
4508
     *
4509
     * EXAMPLE: <code>
4510
     * s(' String speech = "Hi"')->toSpaces(); // '    String speech = "Hi"'
4511
     * </code>
4512
     *
4513
     * @param int $tabLength [optional] <p>Number of spaces to replace each tab with. Default: 4</p>
4514
     *
4515
     * @psalm-mutation-free
4516
     *
4517
     * @return static
4518
     *                <p>Object whose $str has had tabs switched to spaces.</p>
4519
     */
4520
    public function toSpaces(int $tabLength = 4): self
4521
    {
4522
        if ($tabLength === 4) {
18✔
4523
            $tab = '    ';
9✔
4524
        } elseif ($tabLength === 2) {
9✔
4525
            $tab = '  ';
3✔
4526
        } else {
4527
            $tab = \str_repeat(' ', $tabLength);
6✔
4528
        }
4529

4530
        return static::create(
18✔
4531
            \str_replace("\t", $tab, $this->str),
18✔
4532
            $this->encoding
18✔
4533
        );
18✔
4534
    }
4535

4536
    /**
4537
     * Return Stringy object as string, but you can also use (string) for automatically casting the object into a
4538
     * string.
4539
     *
4540
     * EXAMPLE: <code>
4541
     * s('fòôbàř')->toString(); // 'fòôbàř'
4542
     * </code>
4543
     *
4544
     * @psalm-mutation-free
4545
     *
4546
     * @return string
4547
     */
4548
    public function toString(): string
4549
    {
4550
        return (string) $this->str;
2,201✔
4551
    }
4552

4553
    /**
4554
     * Converts each occurrence of some consecutive number of spaces, as
4555
     * defined by $tabLength, to a tab. By default, each 4 consecutive spaces
4556
     * are converted to a tab.
4557
     *
4558
     * EXAMPLE: <code>
4559
     * s('    fòô    bàř')->toTabs(); // '   fòô bàř'
4560
     * </code>
4561
     *
4562
     * @param int $tabLength [optional] <p>Number of spaces to replace with a tab. Default: 4</p>
4563
     *
4564
     * @psalm-mutation-free
4565
     *
4566
     * @return static
4567
     *                <p>Object whose $str has had spaces switched to tabs.</p>
4568
     */
4569
    public function toTabs(int $tabLength = 4): self
4570
    {
4571
        if ($tabLength === 4) {
15✔
4572
            $tab = '    ';
9✔
4573
        } elseif ($tabLength === 2) {
6✔
4574
            $tab = '  ';
3✔
4575
        } else {
4576
            $tab = \str_repeat(' ', $tabLength);
3✔
4577
        }
4578

4579
        return static::create(
15✔
4580
            \str_replace($tab, "\t", $this->str),
15✔
4581
            $this->encoding
15✔
4582
        );
15✔
4583
    }
4584

4585
    /**
4586
     * Converts the first character of each word in the string to uppercase
4587
     * and all other chars to lowercase.
4588
     *
4589
     * EXAMPLE: <code>
4590
     * s('fòô bàř')->toTitleCase(); // 'Fòô Bàř'
4591
     * </code>
4592
     *
4593
     * @psalm-mutation-free
4594
     *
4595
     * @return static
4596
     *                <p>Object with all characters of $str being title-cased.</p>
4597
     */
4598
    public function toTitleCase(): self
4599
    {
4600
        return static::create(
15✔
4601
            $this->utf8::titlecase($this->str, $this->encoding),
15✔
4602
            $this->encoding
15✔
4603
        );
15✔
4604
    }
4605

4606
    /**
4607
     * Returns an ASCII version of the string. A set of non-ASCII characters are
4608
     * replaced with their closest ASCII counterparts, and the rest are removed
4609
     * unless instructed otherwise.
4610
     *
4611
     * EXAMPLE: <code>
4612
     * </code>
4613
     *
4614
     * @param bool   $strict  [optional] <p>Use "transliterator_transliterate()" from PHP-Intl | WARNING: bad
4615
     *                        performance | Default: false</p>
4616
     * @param string $unknown [optional] <p>Character use if character unknown. (default is ?)</p>
4617
     *
4618
     * @psalm-mutation-free
4619
     *
4620
     * @return static
4621
     *                <p>Object whose $str contains only ASCII characters.</p>
4622
     */
4623
    public function toTransliterate(bool $strict = false, string $unknown = '?'): self
4624
    {
4625
        return static::create(
34✔
4626
            $this->ascii::to_transliterate($this->str, $unknown, $strict),
34✔
4627
            $this->encoding
34✔
4628
        );
34✔
4629
    }
4630

4631
    /**
4632
     * Converts all characters in the string to uppercase.
4633
     *
4634
     * EXAMPLE: <code>
4635
     * s('fòôbàř')->toUpperCase(); // 'FÒÔBÀŘ'
4636
     * </code>
4637
     *
4638
     * @param bool        $tryToKeepStringLength [optional] <p>true === try to keep the string length: e.g. ẞ -> ß</p>
4639
     * @param string|null $lang                  [optional] <p>Set the language for special cases: az, el, lt, tr</p>
4640
     *
4641
     * @psalm-mutation-free
4642
     *
4643
     * @return static
4644
     *                <p>Object with all characters of $str being uppercase.</p>
4645
     */
4646
    public function toUpperCase($tryToKeepStringLength = false, $lang = null): self
4647
    {
4648
        return static::create(
26✔
4649
            $this->utf8::strtoupper($this->str, $this->encoding, false, $lang, $tryToKeepStringLength),
26✔
4650
            $this->encoding
26✔
4651
        );
26✔
4652
    }
4653

4654
    /**
4655
     * Returns a string with whitespace removed from the start and end of the
4656
     * string. Supports the removal of unicode whitespace. Accepts an optional
4657
     * string of characters to strip instead of the defaults.
4658
     *
4659
     * EXAMPLE: <code>
4660
     * s('  fòôbàř  ')->trim(); // 'fòôbàř'
4661
     * </code>
4662
     *
4663
     * @param string $chars [optional] <p>String of characters to strip. Default: null</p>
4664
     *
4665
     * @psalm-mutation-free
4666
     *
4667
     * @return static
4668
     *                <p>Object with a trimmed $str.</p>
4669
     */
4670
    public function trim(string $chars = null): self
4671
    {
4672
        return static::create(
36✔
4673
            $this->utf8::trim($this->str, $chars),
36✔
4674
            $this->encoding
36✔
4675
        );
36✔
4676
    }
4677

4678
    /**
4679
     * Returns a string with whitespace removed from the start of the string.
4680
     * Supports the removal of unicode whitespace. Accepts an optional
4681
     * string of characters to strip instead of the defaults.
4682
     *
4683
     * EXAMPLE: <code>
4684
     * s('  fòôbàř  ')->trimLeft(); // 'fòôbàř  '
4685
     * </code>
4686
     *
4687
     * @param string $chars [optional] <p>Optional string of characters to strip. Default: null</p>
4688
     *
4689
     * @psalm-mutation-free
4690
     *
4691
     * @return static
4692
     *                <p>Object with a trimmed $str.</p>
4693
     */
4694
    public function trimLeft(string $chars = null): self
4695
    {
4696
        return static::create(
39✔
4697
            $this->utf8::ltrim($this->str, $chars),
39✔
4698
            $this->encoding
39✔
4699
        );
39✔
4700
    }
4701

4702
    /**
4703
     * Returns a string with whitespace removed from the end of the string.
4704
     * Supports the removal of unicode whitespace. Accepts an optional
4705
     * string of characters to strip instead of the defaults.
4706
     *
4707
     * EXAMPLE: <code>
4708
     * s('  fòôbàř  ')->trimRight(); // '  fòôbàř'
4709
     * </code>
4710
     *
4711
     * @param string $chars [optional] <p>Optional string of characters to strip. Default: null</p>
4712
     *
4713
     * @psalm-mutation-free
4714
     *
4715
     * @return static
4716
     *                <p>Object with a trimmed $str.</p>
4717
     */
4718
    public function trimRight(string $chars = null): self
4719
    {
4720
        return static::create(
39✔
4721
            $this->utf8::rtrim($this->str, $chars),
39✔
4722
            $this->encoding
39✔
4723
        );
39✔
4724
    }
4725

4726
    /**
4727
     * Truncates the string to a given length. If $substring is provided, and
4728
     * truncating occurs, the string is further truncated so that the substring
4729
     * may be appended without exceeding the desired length.
4730
     *
4731
     * EXAMPLE: <code>
4732
     * s('What are your plans today?')->truncate(19, '...'); // 'What are your pl...'
4733
     * </code>
4734
     *
4735
     * @param int    $length    <p>Desired length of the truncated string.</p>
4736
     * @param string $substring [optional] <p>The substring to append if it can fit. Default: ''</p>
4737
     *
4738
     * @psalm-mutation-free
4739
     *
4740
     * @return static
4741
     *                <p>Object with the resulting $str after truncating.</p>
4742
     */
4743
    public function truncate(int $length, string $substring = ''): self
4744
    {
4745
        return static::create(
66✔
4746
            $this->utf8::str_truncate($this->str, $length, $substring, $this->encoding),
66✔
4747
            $this->encoding
66✔
4748
        );
66✔
4749
    }
4750

4751
    /**
4752
     * Returns a lowercase and trimmed string separated by underscores.
4753
     * Underscores are inserted before uppercase characters (with the exception
4754
     * of the first character of the string), and in place of spaces as well as
4755
     * dashes.
4756
     *
4757
     * EXAMPLE: <code>
4758
     * s('TestUCase')->underscored(); // 'test_u_case'
4759
     * </code>
4760
     *
4761
     * @psalm-mutation-free
4762
     *
4763
     * @return static
4764
     *                <p>Object with an underscored $str.</p>
4765
     */
4766
    public function underscored(): self
4767
    {
4768
        return $this->delimit('_');
48✔
4769
    }
4770

4771
    /**
4772
     * Returns an UpperCamelCase version of the supplied string. It trims
4773
     * surrounding spaces, capitalizes letters following digits, spaces, dashes
4774
     * and underscores, and removes spaces, dashes, underscores.
4775
     *
4776
     * EXAMPLE: <code>
4777
     * s('Upper Camel-Case')->upperCamelize(); // 'UpperCamelCase'
4778
     * </code>
4779
     *
4780
     * @psalm-mutation-free
4781
     *
4782
     * @return static
4783
     *                <p>Object with $str in UpperCamelCase.</p>
4784
     */
4785
    public function upperCamelize(): self
4786
    {
4787
        return static::create(
39✔
4788
            $this->utf8::str_upper_camelize($this->str, $this->encoding),
39✔
4789
            $this->encoding
39✔
4790
        );
39✔
4791
    }
4792

4793
    /**
4794
     * Converts the first character of the supplied string to upper case.
4795
     *
4796
     * EXAMPLE: <code>
4797
     * s('σ foo')->upperCaseFirst(); // 'Σ foo'
4798
     * </code>
4799
     *
4800
     * @psalm-mutation-free
4801
     *
4802
     * @return static
4803
     *                <p>Object with the first character of $str being upper case.</p>
4804
     */
4805
    public function upperCaseFirst(): self
4806
    {
4807
        return static::create($this->utf8::ucfirst($this->str, $this->encoding), $this->encoding);
18✔
4808
    }
4809

4810
    /**
4811
     * Simple url-decoding.
4812
     *
4813
     * e.g:
4814
     * 'test+test' => 'test test'
4815
     *
4816
     * EXAMPLE: <code>
4817
     * </code>
4818
     *
4819
     * @psalm-mutation-free
4820
     *
4821
     * @return static
4822
     */
4823
    public function urlDecode(): self
4824
    {
4825
        return static::create(\urldecode($this->str));
1✔
4826
    }
4827

4828
    /**
4829
     * Multi url-decoding + decode HTML entity + fix urlencoded-win1252-chars.
4830
     *
4831
     * e.g:
4832
     * 'test+test'                     => 'test test'
4833
     * 'D&#252;sseldorf'               => 'Düsseldorf'
4834
     * 'D%FCsseldorf'                  => 'Düsseldorf'
4835
     * 'D&#xFC;sseldorf'               => 'Düsseldorf'
4836
     * 'D%26%23xFC%3Bsseldorf'         => 'Düsseldorf'
4837
     * 'Düsseldorf'                   => 'Düsseldorf'
4838
     * 'D%C3%BCsseldorf'               => 'Düsseldorf'
4839
     * 'D%C3%83%C2%BCsseldorf'         => 'Düsseldorf'
4840
     * 'D%25C3%2583%25C2%25BCsseldorf' => 'Düsseldorf'
4841
     *
4842
     * EXAMPLE: <code>
4843
     * </code>
4844
     *
4845
     * @psalm-mutation-free
4846
     *
4847
     * @return static
4848
     */
4849
    public function urlDecodeMulti(): self
4850
    {
4851
        return static::create($this->utf8::urldecode($this->str));
1✔
4852
    }
4853

4854
    /**
4855
     * Simple url-decoding.
4856
     *
4857
     * e.g:
4858
     * 'test+test' => 'test+test
4859
     *
4860
     * EXAMPLE: <code>
4861
     * </code>
4862
     *
4863
     * @psalm-mutation-free
4864
     *
4865
     * @return static
4866
     */
4867
    public function urlDecodeRaw(): self
4868
    {
4869
        return static::create(\rawurldecode($this->str));
1✔
4870
    }
4871

4872
    /**
4873
     * Multi url-decoding + decode HTML entity + fix urlencoded-win1252-chars.
4874
     *
4875
     * e.g:
4876
     * 'test+test'                     => 'test+test'
4877
     * 'D&#252;sseldorf'               => 'Düsseldorf'
4878
     * 'D%FCsseldorf'                  => 'Düsseldorf'
4879
     * 'D&#xFC;sseldorf'               => 'Düsseldorf'
4880
     * 'D%26%23xFC%3Bsseldorf'         => 'Düsseldorf'
4881
     * 'Düsseldorf'                   => 'Düsseldorf'
4882
     * 'D%C3%BCsseldorf'               => 'Düsseldorf'
4883
     * 'D%C3%83%C2%BCsseldorf'         => 'Düsseldorf'
4884
     * 'D%25C3%2583%25C2%25BCsseldorf' => 'Düsseldorf'
4885
     *
4886
     * EXAMPLE: <code>
4887
     * </code>
4888
     *
4889
     * @psalm-mutation-free
4890
     *
4891
     * @return static
4892
     */
4893
    public function urlDecodeRawMulti(): self
4894
    {
4895
        return static::create($this->utf8::rawurldecode($this->str));
1✔
4896
    }
4897

4898
    /**
4899
     * Simple url-encoding.
4900
     *
4901
     * e.g:
4902
     * 'test test' => 'test+test'
4903
     *
4904
     * EXAMPLE: <code>
4905
     * </code>
4906
     *
4907
     * @psalm-mutation-free
4908
     *
4909
     * @return static
4910
     */
4911
    public function urlEncode(): self
4912
    {
4913
        return static::create(\urlencode($this->str));
1✔
4914
    }
4915

4916
    /**
4917
     * Simple url-encoding.
4918
     *
4919
     * e.g:
4920
     * 'test test' => 'test%20test'
4921
     *
4922
     * EXAMPLE: <code>
4923
     * </code>
4924
     *
4925
     * @psalm-mutation-free
4926
     *
4927
     * @return static
4928
     */
4929
    public function urlEncodeRaw(): self
4930
    {
4931
        return static::create(\rawurlencode($this->str));
1✔
4932
    }
4933

4934
    /**
4935
     * Converts the string into an URL slug. This includes replacing non-ASCII
4936
     * characters with their closest ASCII equivalents, removing remaining
4937
     * non-ASCII and non-alphanumeric characters, and replacing whitespace with
4938
     * $separator. The separator defaults to a single dash, and the string
4939
     * is also converted to lowercase.
4940
     *
4941
     * EXAMPLE: <code>
4942
     * s('Using strings like fòô bàř - 1$')->urlify(); // 'using-strings-like-foo-bar-1-dollar'
4943
     * </code>
4944
     *
4945
     * @param string                $separator    [optional] <p>The string used to replace whitespace. Default: '-'</p>
4946
     * @param string                $language     [optional] <p>The language for the url. Default: 'en'</p>
4947
     * @param array<string, string> $replacements [optional] <p>A map of replaceable strings.</p>
4948
     * @param bool                  $strToLower   [optional] <p>string to lower. Default: true</p>
4949
     *
4950
     * @psalm-mutation-free
4951
     *
4952
     * @return static
4953
     *                <p>Object whose $str has been converted to an URL slug.</p>
4954
     *
4955
     * @psalm-suppress ImpureMethodCall :/
4956
     */
4957
    public function urlify(
4958
        string $separator = '-',
4959
        string $language = 'en',
4960
        array $replacements = [],
4961
        bool $strToLower = true
4962
    ): self {
4963
        // init
4964
        $str = $this->str;
32✔
4965

4966
        foreach ($replacements as $from => $to) {
32✔
4967
            $str = \str_replace($from, $to, $str);
32✔
4968
        }
4969

4970
        return static::create(
32✔
4971
            URLify::slug(
32✔
4972
                $str,
32✔
4973
                $language,
32✔
4974
                $separator,
32✔
4975
                $strToLower
32✔
4976
            ),
32✔
4977
            $this->encoding
32✔
4978
        );
32✔
4979
    }
4980

4981
    /**
4982
     * Converts the string into an valid UTF-8 string.
4983
     *
4984
     * EXAMPLE: <code>
4985
     * s('Düsseldorf')->utf8ify(); // 'Düsseldorf'
4986
     * </code>
4987
     *
4988
     * @psalm-mutation-free
4989
     *
4990
     * @return static
4991
     */
4992
    public function utf8ify(): self
4993
    {
4994
        return static::create($this->utf8::cleanup($this->str), $this->encoding);
2✔
4995
    }
4996

4997
    /**
4998
     * Convert a string into an array of words.
4999
     *
5000
     * EXAMPLE: <code>
5001
     * </code>
5002
     *
5003
     * @param string   $char_list           [optional] <p>Additional chars for the definition of "words".</p>
5004
     * @param bool     $remove_empty_values [optional] <p>Remove empty values.</p>
5005
     * @param int|null $remove_short_values [optional] <p>The min. string length or null to disable</p>
5006
     *
5007
     * @psalm-mutation-free
5008
     *
5009
     * @return static[]
5010
     *
5011
     * @phpstan-return array<int,static>
5012
     */
5013
    public function words(
5014
        string $char_list = '',
5015
        bool $remove_empty_values = false,
5016
        int $remove_short_values = null
5017
    ): array {
5018
        if ($remove_short_values === null) {
14✔
5019
            $strings = $this->utf8::str_to_words(
14✔
5020
                $this->str,
14✔
5021
                $char_list,
14✔
5022
                $remove_empty_values
14✔
5023
            );
14✔
5024
        } else {
5025
            $strings = $this->utf8::str_to_words(
2✔
5026
                $this->str,
2✔
5027
                $char_list,
2✔
5028
                $remove_empty_values,
2✔
5029
                $remove_short_values
2✔
5030
            );
2✔
5031
        }
5032

5033
        /** @noinspection AlterInForeachInspection */
5034
        foreach ($strings as &$string) {
14✔
5035
            $string = static::create($string);
14✔
5036
        }
5037

5038
        /** @noinspection PhpSillyAssignmentInspection */
5039
        /** @var static[] $strings */
5040
        $strings = $strings;
14✔
5041

5042
        return $strings;
14✔
5043
    }
5044

5045
    /**
5046
     * Convert a string into an collection of words.
5047
     *
5048
     * EXAMPLE: <code>
5049
     * S::create('中文空白 oöäü#s')->wordsCollection('#', true)->toStrings(); // ['中文空白', 'oöäü#s']
5050
     * </code>
5051
     *
5052
     * @param string   $char_list           [optional] <p>Additional chars for the definition of "words".</p>
5053
     * @param bool     $remove_empty_values [optional] <p>Remove empty values.</p>
5054
     * @param int|null $remove_short_values [optional] <p>The min. string length or null to disable</p>
5055
     *
5056
     * @psalm-mutation-free
5057
     *
5058
     * @return CollectionStringy|static[]
5059
     *                                    <p>An collection of Stringy objects.</p>
5060
     *
5061
     * @phpstan-return CollectionStringy<int,static>
5062
     */
5063
    public function wordsCollection(
5064
        string $char_list = '',
5065
        bool $remove_empty_values = false,
5066
        int $remove_short_values = null
5067
    ): CollectionStringy {
5068
        /**
5069
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to the collection class
5070
         */
5071
        return CollectionStringy::create(
2✔
5072
            $this->words(
2✔
5073
                $char_list,
2✔
5074
                $remove_empty_values,
2✔
5075
                $remove_short_values
2✔
5076
            )
2✔
5077
        );
2✔
5078
    }
5079

5080
    /**
5081
     * Surrounds $str with the given substring.
5082
     *
5083
     * EXAMPLE: <code>
5084
     * </code>
5085
     *
5086
     * @param string $substring <p>The substring to add to both sides.</P>
5087
     *
5088
     * @psalm-mutation-free
5089
     *
5090
     * @return static
5091
     *                <p>Object whose $str had the substring both prepended and appended.</p>
5092
     */
5093
    public function wrap(string $substring): self
5094
    {
5095
        return $this->surround($substring);
10✔
5096
    }
5097

5098
    /**
5099
     * Returns the replacements for the toAscii() method.
5100
     *
5101
     * @psalm-mutation-free
5102
     *
5103
     * @return array<string, array<int, string>>
5104
     *                                           <p>An array of replacements.</p>
5105
     *
5106
     * @deprecated   this is only here for backward-compatibly reasons
5107
     */
5108
    protected function charsArray(): array
5109
    {
5110
        return $this->ascii::charsArrayWithMultiLanguageValues();
1✔
5111
    }
5112

5113
    /**
5114
     * Returns true if $str matches the supplied pattern, false otherwise.
5115
     *
5116
     * @param string $pattern <p>Regex pattern to match against.</p>
5117
     *
5118
     * @psalm-mutation-free
5119
     *
5120
     * @return bool
5121
     *              <p>Whether or not $str matches the pattern.</p>
5122
     */
5123
    protected function matchesPattern(string $pattern): bool
5124
    {
5125
        return $this->utf8::str_matches_pattern($this->str, $pattern);
24✔
5126
    }
5127
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc