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

voku / Stringy / 25975435543

16 May 2026 11:10PM UTC coverage: 98.033% (+0.1%) from 97.891%
25975435543

Pull #55

github

web-flow
Merge fded9eb84 into 3357517e5
Pull Request #55: Restore Stringy fast paths and remove mutation-only regressions

30 of 31 new or added lines in 1 file covered. (96.77%)

12 existing lines in 1 file now uncovered.

997 of 1017 relevant lines covered (98.03%)

71.51 hits per line

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

98.01
/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
    use JsonSerializableReturnTypeTrait;
37

38
    /**
39
     * An instance's string.
40
     *
41
     * @var string
42
     */
43
    protected $str;
44

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

53
    /**
54
     * @var UTF8
55
     */
56
    private $utf8;
57

58
    /**
59
     * @var ASCII
60
     */
61
    private $ascii;
62

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

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

98
        $this->str = (string) $str;
3,704✔
99

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

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

112
        if ($encoding !== 'UTF-8') {
3,704✔
113
            $this->encoding = $this->utf8::normalize_encoding($encoding, 'UTF-8');
2,520✔
114
        } else {
115
            $this->encoding = $encoding;
2,764✔
116
        }
117
    }
118

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

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

155
        unset($strArray[0]);
4✔
156

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

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

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

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

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

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

285
        return static::create($this->str . $suffix, $this->encoding);
21✔
286
    }
287

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

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

327
        $str = $this->utf8::get_random_string($length, $possibleChars);
6✔
328

329
        return $this->append($str);
6✔
330
    }
331

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

358
        return static::create($this->str . $suffixStr, $this->encoding);
8✔
359
    }
360

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

381
    /**
382
     * Returns the character at $index, with indexes starting at 0.
383
     *
384
     * EXAMPLE: <code>
385
     * s('fòôbàř')->at(3); // 'b'
386
     * </code>
387
     *
388
     * @param int $index <p>Position of the character.</p>
389
     *
390
     * @psalm-mutation-free
391
     *
392
     * @return static
393
     *                <p>The character at $index.</p>
394
     */
395
    public function at(int $index): self
396
    {
397
        return static::create(
32✔
398
            $this->utf8::substr($this->str, $index, 1, $this->encoding),
32✔
399
            $this->encoding
32✔
400
        );
32✔
401
    }
402

403
    /**
404
     * Decode the base64 encoded string.
405
     *
406
     * EXAMPLE: <code>
407
     * </code>
408
     *
409
     * @psalm-mutation-free
410
     *
411
     * @return self
412
     */
413
    public function base64Decode(): self
414
    {
415
        return static::create(
2✔
416
            \base64_decode($this->str, true),
2✔
417
            $this->encoding
2✔
418
        );
2✔
419
    }
420

421
    /**
422
     * Encode the string to base64.
423
     *
424
     * EXAMPLE: <code>
425
     * </code>
426
     *
427
     * @psalm-mutation-free
428
     *
429
     * @return self
430
     */
431
    public function base64Encode(): self
432
    {
433
        return static::create(
2✔
434
            \base64_encode($this->str),
2✔
435
            $this->encoding
2✔
436
        );
2✔
437
    }
438

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

466
    /**
467
     * Return part of the string occurring before a specific string.
468
     *
469
     * EXAMPLE: <code>
470
     * </code>
471
     *
472
     * @param string $string <p>The delimiting string.</p>
473
     *
474
     * @psalm-mutation-free
475
     *
476
     * @return static
477
     */
478
    public function before(string $string): self
479
    {
480
        return $this->beforeFirst($string);
5✔
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(
7✔
500
            $this->utf8::str_substr_before_first_separator(
7✔
501
                $this->str,
7✔
502
                $separator,
7✔
503
                $this->encoding
7✔
504
            ),
7✔
505
            $this->encoding
7✔
506
        );
7✔
507
    }
508

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

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

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

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

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

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

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

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

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

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

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

725
        $chunks = $this->utf8::str_split($this->str, $length);
15✔
726

727
        foreach ($chunks as &$value) {
15✔
728
            $value = static::create($value, $this->encoding);
13✔
729
        }
730

731
        /** @noinspection PhpSillyAssignmentInspection */
732
        /** @var static[] $chunks */
733
        $chunks = $chunks;
15✔
734

735
        return $chunks;
15✔
736
    }
737

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1057
    /**
1058
     * Encode the given string into the given $encoding + set the internal character encoding.
1059
     *
1060
     * EXAMPLE: <code>
1061
     * </code>
1062
     *
1063
     * @param string $new_encoding         <p>The desired character encoding.</p>
1064
     * @param bool   $auto_detect_encoding [optional] <p>Auto-detect the current string-encoding</p>
1065
     *
1066
     * @psalm-mutation-free
1067
     *
1068
     * @return static
1069
     */
1070
    public function encode(string $new_encoding, bool $auto_detect_encoding = false): self
1071
    {
1072
        $str = $this->utf8::encode(
3✔
1073
            $new_encoding,
3✔
1074
            $this->str,
3✔
1075
            $auto_detect_encoding,
3✔
1076
            $auto_detect_encoding ? '' : $this->encoding
3✔
1077
        );
3✔
1078

1079
        return new static($str, $new_encoding);
3✔
1080
    }
1081

1082
    /**
1083
     * Encrypt the string.
1084
     *
1085
     * EXAMPLE: <code>
1086
     * </code>
1087
     *
1088
     * @param string $password <p>The key for encrypting</p>
1089
     *
1090
     * @psalm-mutation-free
1091
     *
1092
     * @return static
1093
     */
1094
    public function encrypt(string $password): self
1095
    {
1096
        /**
1097
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to vendor stuff
1098
         */
1099
        return new static(
4✔
1100
            Crypto::encryptWithPassword($this->str, $password),
4✔
1101
            $this->encoding
4✔
1102
        );
4✔
1103
    }
1104

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

1128
        return $this->utf8::str_iends_with($this->str, $substring);
44✔
1129
    }
1130

1131
    /**
1132
     * Returns true if the string ends with any of $substrings, false otherwise.
1133
     * By default, the comparison is case-sensitive, but can be made insensitive
1134
     * by setting $caseSensitive to false.
1135
     *
1136
     * EXAMPLE: <code>
1137
     * s('fòôbàř')->endsWithAny(['bàř', 'baz'], true); // true
1138
     * </code>
1139
     *
1140
     * @param string[] $substrings    <p>Substrings to look for.</p>
1141
     * @param bool     $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
1142
     *
1143
     * @psalm-mutation-free
1144
     *
1145
     * @return bool
1146
     *              <p>Whether or not $str ends with $substring.</p>
1147
     */
1148
    public function endsWithAny(array $substrings, bool $caseSensitive = true): bool
1149
    {
1150
        if ($caseSensitive) {
34✔
1151
            return $this->utf8::str_ends_with_any($this->str, $substrings);
22✔
1152
        }
1153

1154
        return $this->utf8::str_iends_with_any($this->str, $substrings);
12✔
1155
    }
1156

1157
    /**
1158
     * Ensures that the string begins with $substring. If it doesn't, it's
1159
     * prepended.
1160
     *
1161
     * EXAMPLE: <code>
1162
     * s('foobar')->ensureLeft('http://'); // 'http://foobar'
1163
     * </code>
1164
     *
1165
     * @param string $substring <p>The substring to add if not present.</p>
1166
     *
1167
     * @psalm-mutation-free
1168
     *
1169
     * @return static
1170
     *                <p>Object with its $str prefixed by the $substring.</p>
1171
     */
1172
    public function ensureLeft(string $substring): self
1173
    {
1174
        return static::create(
30✔
1175
            $this->utf8::str_ensure_left($this->str, $substring),
30✔
1176
            $this->encoding
30✔
1177
        );
30✔
1178
    }
1179

1180
    /**
1181
     * Ensures that the string ends with $substring. If it doesn't, it's appended.
1182
     *
1183
     * EXAMPLE: <code>
1184
     * s('foobar')->ensureRight('.com'); // 'foobar.com'
1185
     * </code>
1186
     *
1187
     * @param string $substring <p>The substring to add if not present.</p>
1188
     *
1189
     * @psalm-mutation-free
1190
     *
1191
     * @return static
1192
     *                <p>Object with its $str suffixed by the $substring.</p>
1193
     */
1194
    public function ensureRight(string $substring): self
1195
    {
1196
        return static::create(
30✔
1197
            $this->utf8::str_ensure_right($this->str, $substring),
30✔
1198
            $this->encoding
30✔
1199
        );
30✔
1200
    }
1201

1202
    /**
1203
     * Create a escape html version of the string via "htmlspecialchars()".
1204
     *
1205
     * EXAMPLE: <code>
1206
     * s('<∂∆ onerror="alert(xss)">')->escape(); // '&lt;∂∆ onerror=&quot;alert(xss)&quot;&gt;'
1207
     * </code>
1208
     *
1209
     * @psalm-mutation-free
1210
     *
1211
     * @return static
1212
     */
1213
    public function escape(): self
1214
    {
1215
        return static::create(
12✔
1216
            $this->utf8::htmlspecialchars(
12✔
1217
                $this->str,
12✔
1218
                \ENT_QUOTES | \ENT_SUBSTITUTE,
12✔
1219
                $this->encoding
12✔
1220
            ),
12✔
1221
            $this->encoding
12✔
1222
        );
12✔
1223
    }
1224

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

1250
        /** @phpstan-ignore-next-line - FP -> non-empty-string is already checked */
1251
        $strings = \explode($delimiter, $this->str, $limit);
3✔
1252
        /** @phpstan-ignore-next-line - if "$delimiter" is an empty string, then "explode()" will return "false" */
1253
        if ($strings === false) {
3✔
1254
            $strings = [];
×
1255
        }
1256

1257
        return \array_map(
3✔
1258
            function ($str) {
3✔
1259
                return new static($str, $this->encoding);
3✔
1260
            },
3✔
1261
            $strings
3✔
1262
        );
3✔
1263
    }
1264

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

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

1327
    /**
1328
     * Returns the first $n characters of the string.
1329
     *
1330
     * EXAMPLE: <code>
1331
     * s('fòôbàř')->first(3); // 'fòô'
1332
     * </code>
1333
     *
1334
     * @param int $n <p>Number of characters to retrieve from the start.</p>
1335
     *
1336
     * @psalm-mutation-free
1337
     *
1338
     * @return static
1339
     *                <p>Object with its $str being the first $n chars.</p>
1340
     */
1341
    public function first(int $n): self
1342
    {
1343
        if ($n <= 0) {
37✔
1344
            return static::create('', $this->encoding);
12✔
1345
        }
1346

1347
        return static::create(
25✔
1348
            $this->utf8::first_char($this->str, $n, $this->encoding),
25✔
1349
            $this->encoding
25✔
1350
        );
25✔
1351
    }
1352

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

1388
        if (\strpos($this->str, '%:') !== false) {
11✔
1389
            $offset = null;
9✔
1390
            $replacementLength = 0;
9✔
1391
            /** @noinspection AlterInForeachInspection */
1392
            foreach ($args as $key => &$arg) {
9✔
1393
                if (!\is_array($arg)) {
9✔
1394
                    continue;
4✔
1395
                }
1396

1397
                foreach ($arg as $name => $param) {
9✔
1398
                    $name = (string) $name;
9✔
1399

1400
                    if (\strpos($name, '%:') !== 0) {
9✔
1401
                        $nameTmp = '%:' . $name;
9✔
1402
                    } else {
NEW
1403
                        $nameTmp = $name;
×
1404
                    }
1405

1406
                    if ($offset === null) {
9✔
1407
                        $offset = \strpos($str, $nameTmp);
9✔
1408
                    } else {
1409
                        $offset = \strpos($str, $nameTmp, $offset + $replacementLength);
7✔
1410
                    }
1411
                    if ($offset === false) {
9✔
1412
                        continue;
5✔
1413
                    }
1414

1415
                    unset($arg[$name]);
9✔
1416

1417
                    $replacementLength = \strlen((string) $param);
9✔
1418
                    $str = \substr_replace($str, (string) $param, (int) $offset, \strlen($nameTmp));
9✔
1419
                }
1420

1421
                unset($args[$key]);
9✔
1422
            }
1423
        }
1424

1425
        $str = \str_replace('%:', '%%:', $str);
11✔
1426

1427
        return static::create(
11✔
1428
            \sprintf($str, ...$args),
11✔
1429
            $this->encoding
11✔
1430
        );
11✔
1431
    }
1432

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

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

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

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

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

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

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

1563
        return static::create(
3✔
1564
            $string,
3✔
1565
            $this->encoding
3✔
1566
        );
3✔
1567
    }
1568

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

1589
        return static::create(
2✔
1590
            $string,
2✔
1591
            $this->encoding
2✔
1592
        );
2✔
1593
    }
1594

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

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

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

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

1781
        return \stripos($str, $this->str) !== false;
1✔
1782
    }
1783

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

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

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

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

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

1922
    /**
1923
     * Returns true if the string contains the $pattern, otherwise false.
1924
     *
1925
     * WARNING: Asterisks ("*") are translated into (".*") zero-or-more regular
1926
     * expression wildcards.
1927
     *
1928
     * EXAMPLE: <code>
1929
     * s('Foo\\Bar\\Lall')->is('*\\Bar\\*'); // true
1930
     * </code>
1931
     *
1932
     * @credit Originally from Laravel, thanks Taylor.
1933
     *
1934
     * @param string $pattern <p>The string or pattern to match against.</p>
1935
     *
1936
     * @psalm-mutation-free
1937
     *
1938
     * @return bool
1939
     *              <p>Whether or not we match the provided pattern.</p>
1940
     */
1941
    public function is(string $pattern): bool
1942
    {
1943
        $quotedPattern = \preg_quote($pattern, '/');
27✔
1944
        $replaceWildCards = \str_replace('\*', '.*', $quotedPattern);
27✔
1945

1946
        return $this->matchesPattern('^' . $replaceWildCards . '\z');
27✔
1947
    }
1948

1949
    /**
1950
     * Returns true if the string contains only alphabetic chars, false otherwise.
1951
     *
1952
     * EXAMPLE: <code>
1953
     * s('丹尼爾')->isAlpha(); // true
1954
     * </code>
1955
     *
1956
     * @psalm-mutation-free
1957
     *
1958
     * @return bool
1959
     *              <p>Whether or not $str contains only alphabetic chars.</p>
1960
     */
1961
    public function isAlpha(): bool
1962
    {
1963
        return $this->utf8::is_alpha($this->str);
30✔
1964
    }
1965

1966
    /**
1967
     * Returns true if the string contains only alphabetic and numeric chars, false otherwise.
1968
     *
1969
     * EXAMPLE: <code>
1970
     * s('دانيال1')->isAlphanumeric(); // true
1971
     * </code>
1972
     *
1973
     * @psalm-mutation-free
1974
     *
1975
     * @return bool
1976
     *              <p>Whether or not $str contains only alphanumeric chars.</p>
1977
     */
1978
    public function isAlphanumeric(): bool
1979
    {
1980
        return $this->utf8::is_alphanumeric($this->str);
39✔
1981
    }
1982

1983
    /**
1984
     * Checks if a string is 7 bit ASCII.
1985
     *
1986
     * EXAMPLE: <code>s('白')->isAscii; // false</code>
1987
     *
1988
     * @psalm-mutation-free
1989
     *
1990
     * @return bool
1991
     *              <p>
1992
     *              <strong>true</strong> if it is ASCII<br>
1993
     *              <strong>false</strong> otherwise
1994
     *              </p>
1995
     *
1996
     * @noinspection GetSetMethodCorrectnessInspection
1997
     */
1998
    public function isAscii(): bool
1999
    {
UNCOV
2000
        return $this->utf8::is_ascii($this->str);
×
2001
    }
2002

2003
    /**
2004
     * Returns true if the string is base64 encoded, false otherwise.
2005
     *
2006
     * EXAMPLE: <code>
2007
     * s('Zm9vYmFy')->isBase64(); // true
2008
     * </code>
2009
     *
2010
     * @param bool $emptyStringIsValid
2011
     *
2012
     * @psalm-mutation-free
2013
     *
2014
     * @return bool
2015
     *              <p>Whether or not $str is base64 encoded.</p>
2016
     */
2017
    public function isBase64($emptyStringIsValid = true): bool
2018
    {
2019
        return $this->utf8::is_base64($this->str, $emptyStringIsValid);
21✔
2020
    }
2021

2022
    /**
2023
     * Check if the input is binary... (is look like a hack).
2024
     *
2025
     * EXAMPLE: <code>s(01)->isBinary(); // true</code>
2026
     *
2027
     * @psalm-mutation-free
2028
     *
2029
     * @return bool
2030
     */
2031
    public function isBinary(): bool
2032
    {
2033
        return $this->utf8::is_binary($this->str);
1✔
2034
    }
2035

2036
    /**
2037
     * Returns true if the string contains only whitespace chars, false otherwise.
2038
     *
2039
     * EXAMPLE: <code>
2040
     * s("\n\t  \v\f")->isBlank(); // true
2041
     * </code>
2042
     *
2043
     * @psalm-mutation-free
2044
     *
2045
     * @return bool
2046
     *              <p>Whether or not $str contains only whitespace characters.</p>
2047
     */
2048
    public function isBlank(): bool
2049
    {
2050
        return $this->utf8::is_blank($this->str);
45✔
2051
    }
2052

2053
    /**
2054
     * Checks if the given string is equal to any "Byte Order Mark".
2055
     *
2056
     * WARNING: Use "s::string_has_bom()" if you will check BOM in a string.
2057
     *
2058
     * EXAMPLE: <code>s->("\xef\xbb\xbf")->isBom(); // true</code>
2059
     *
2060
     * @psalm-mutation-free
2061
     *
2062
     * @return bool
2063
     *              <p><strong>true</strong> if the $utf8_chr is Byte Order Mark, <strong>false</strong> otherwise.</p>
2064
     */
2065
    public function isBom(): bool
2066
    {
UNCOV
2067
        return $this->utf8::is_bom($this->str);
×
2068
    }
2069

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

2099
    /**
2100
     * Determine whether the string is considered to be empty.
2101
     *
2102
     * A variable is considered empty if it does not exist or if its value equals FALSE.
2103
     *
2104
     * EXAMPLE: <code>
2105
     * s('')->isEmpty(); // true
2106
     * </code>
2107
     *
2108
     * @psalm-mutation-free
2109
     *
2110
     * @return bool
2111
     *              <p>Whether or not $str is empty().</p>
2112
     */
2113
    public function isEmpty(): bool
2114
    {
2115
        return $this->utf8::is_empty($this->str);
10✔
2116
    }
2117

2118
    /**
2119
     * Determine whether the string is equals to $str.
2120
     * Alias for isEqualsCaseSensitive()
2121
     *
2122
     * EXAMPLE: <code>
2123
     * s('foo')->isEquals('foo'); // true
2124
     * </code>
2125
     *
2126
     * @param string|Stringy ...$str
2127
     *
2128
     * @psalm-mutation-free
2129
     *
2130
     * @return bool
2131
     */
2132
    public function isEquals(...$str): bool
2133
    {
2134
        return $this->isEqualsCaseSensitive(...$str);
13✔
2135
    }
2136

2137
    /**
2138
     * Determine whether the string is equals to $str.
2139
     *
2140
     * EXAMPLE: <code>
2141
     * </code>
2142
     *
2143
     * @param float|int|string|Stringy ...$str <p>The string to compare.</p>
2144
     *
2145
     * @psalm-mutation-free
2146
     *
2147
     * @return bool
2148
     *              <p>Whether or not $str is equals.</p>
2149
     */
2150
    public function isEqualsCaseInsensitive(...$str): bool
2151
    {
2152
        $strUpper = $this->toUpperCase()->str;
4✔
2153

2154
        foreach ($str as $strTmp) {
4✔
2155
            /**
2156
             * @psalm-suppress RedundantConditionGivenDocblockType - wait for union-types :)
2157
             */
2158
            if ($strTmp instanceof self) {
4✔
UNCOV
2159
                if ($strUpper !== $strTmp->toUpperCase()->str) {
×
UNCOV
2160
                    return false;
×
2161
                }
2162
            } elseif (\is_scalar($strTmp)) {
4✔
2163
                if ($strUpper !== $this->utf8::strtoupper((string) $strTmp, $this->encoding)) {
4✔
2164
                    return false;
3✔
2165
                }
2166
            } else {
UNCOV
2167
                throw new \InvalidArgumentException('expected: int|float|string|Stringy -> given: ' . \print_r($strTmp, true) . ' [' . \gettype($strTmp) . ']');
×
2168
            }
2169
        }
2170

2171
        return true;
4✔
2172
    }
2173

2174
    /**
2175
     * Determine whether the string is equals to $str.
2176
     *
2177
     * EXAMPLE: <code>
2178
     * </code>
2179
     *
2180
     * @param float|int|string|Stringy ...$str <p>The string to compare.</p>
2181
     *
2182
     * @psalm-mutation-free
2183
     *
2184
     * @return bool
2185
     *              <p>Whether or not $str is equals.</p>
2186
     */
2187
    public function isEqualsCaseSensitive(...$str): bool
2188
    {
2189
        foreach ($str as $strTmp) {
15✔
2190
            /**
2191
             * @psalm-suppress RedundantConditionGivenDocblockType - wait for union-types :)
2192
             */
2193
            if ($strTmp instanceof self) {
15✔
2194
                if ($this->str !== $strTmp->str) {
2✔
2195
                    return false;
1✔
2196
                }
2197
            } elseif (\is_scalar($strTmp)) {
13✔
2198
                if ($this->str !== (string) $strTmp) {
13✔
2199
                    return false;
10✔
2200
                }
2201
            } else {
UNCOV
2202
                throw new \InvalidArgumentException('expected: int|float|string|Stringy -> given: ' . \print_r($strTmp, true) . ' [' . \gettype($strTmp) . ']');
×
2203
            }
2204
        }
2205

2206
        return true;
4✔
2207
    }
2208

2209
    /**
2210
     * Returns true if the string contains only hexadecimal chars, false otherwise.
2211
     *
2212
     * EXAMPLE: <code>
2213
     * s('A102F')->isHexadecimal(); // true
2214
     * </code>
2215
     *
2216
     * @psalm-mutation-free
2217
     *
2218
     * @return bool
2219
     *              <p>Whether or not $str contains only hexadecimal chars.</p>
2220
     */
2221
    public function isHexadecimal(): bool
2222
    {
2223
        return $this->utf8::is_hexadecimal($this->str);
39✔
2224
    }
2225

2226
    /**
2227
     * Returns true if the string contains HTML-Tags, false otherwise.
2228
     *
2229
     * EXAMPLE: <code>
2230
     * s('<h1>foo</h1>')->isHtml(); // true
2231
     * </code>
2232
     *
2233
     * @psalm-mutation-free
2234
     *
2235
     * @return bool
2236
     *              <p>Whether or not $str contains HTML-Tags.</p>
2237
     */
2238
    public function isHtml(): bool
2239
    {
2240
        return $this->utf8::is_html($this->str);
2✔
2241
    }
2242

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

2270
    /**
2271
     * Returns true if the string contains only lower case chars, false otherwise.
2272
     *
2273
     * EXAMPLE: <code>
2274
     * s('fòôbàř')->isLowerCase(); // true
2275
     * </code>
2276
     *
2277
     * @psalm-mutation-free
2278
     *
2279
     * @return bool
2280
     *              <p>Whether or not $str contains only lower case characters.</p>
2281
     */
2282
    public function isLowerCase(): bool
2283
    {
2284
        return $this->utf8::is_lowercase($this->str);
24✔
2285
    }
2286

2287
    /**
2288
     * Determine whether the string is considered to be NOT empty.
2289
     *
2290
     * A variable is considered NOT empty if it does exist or if its value equals TRUE.
2291
     *
2292
     * EXAMPLE: <code>
2293
     * s('')->isNotEmpty(); // false
2294
     * </code>
2295
     *
2296
     * @psalm-mutation-free
2297
     *
2298
     * @return bool
2299
     *              <p>Whether or not $str is empty().</p>
2300
     */
2301
    public function isNotEmpty(): bool
2302
    {
2303
        return !$this->utf8::is_empty($this->str);
10✔
2304
    }
2305

2306
    /**
2307
     * Determine if the string is composed of numeric characters.
2308
     *
2309
     * EXAMPLE: <code>
2310
     * </code>
2311
     *
2312
     * @psalm-mutation-free
2313
     *
2314
     * @return bool
2315
     */
2316
    public function isNumeric(): bool
2317
    {
2318
        return \is_numeric($this->str);
4✔
2319
    }
2320

2321
    /**
2322
     * Determine if the string is composed of printable (non-invisible) characters.
2323
     *
2324
     * EXAMPLE: <code>
2325
     * </code>
2326
     *
2327
     * @psalm-mutation-free
2328
     *
2329
     * @return bool
2330
     */
2331
    public function isPrintable(): bool
2332
    {
2333
        return $this->utf8::is_printable($this->str);
3✔
2334
    }
2335

2336
    /**
2337
     * Determine if the string is composed of punctuation characters.
2338
     *
2339
     * EXAMPLE: <code>
2340
     * </code>
2341
     *
2342
     * @psalm-mutation-free
2343
     *
2344
     * @return bool
2345
     */
2346
    public function isPunctuation(): bool
2347
    {
2348
        return $this->utf8::is_punctuation($this->str);
3✔
2349
    }
2350

2351
    /**
2352
     * Returns true if the string is serialized, false otherwise.
2353
     *
2354
     * EXAMPLE: <code>
2355
     * s('a:1:{s:3:"foo";s:3:"bar";}')->isSerialized(); // true
2356
     * </code>
2357
     *
2358
     * @psalm-mutation-free
2359
     *
2360
     * @return bool
2361
     *              <p>Whether or not $str is serialized.</p>
2362
     */
2363
    public function isSerialized(): bool
2364
    {
2365
        return $this->utf8::is_serialized($this->str);
21✔
2366
    }
2367

2368
    /**
2369
     * Check if two strings are similar.
2370
     *
2371
     * EXAMPLE: <code>
2372
     * </code>
2373
     *
2374
     * @param string $str                     <p>The string to compare against.</p>
2375
     * @param float  $minPercentForSimilarity [optional] <p>The percentage of needed similarity. | Default: 80%</p>
2376
     *
2377
     * @psalm-mutation-free
2378
     *
2379
     * @return bool
2380
     */
2381
    public function isSimilar(string $str, float $minPercentForSimilarity = 80.0): bool
2382
    {
2383
        return $this->similarity($str) >= $minPercentForSimilarity;
3✔
2384
    }
2385

2386
    /**
2387
     * Returns true if the string contains only lower case chars, false
2388
     * otherwise.
2389
     *
2390
     * EXAMPLE: <code>
2391
     * s('FÒÔBÀŘ')->isUpperCase(); // true
2392
     * </code>
2393
     *
2394
     * @psalm-mutation-free
2395
     *
2396
     * @return bool
2397
     *              <p>Whether or not $str contains only lower case characters.</p>
2398
     */
2399
    public function isUpperCase(): bool
2400
    {
2401
        return $this->utf8::is_uppercase($this->str);
24✔
2402
    }
2403

2404
    /**
2405
     * /**
2406
     * Check if $url is an correct url.
2407
     *
2408
     * @param bool $disallow_localhost
2409
     *
2410
     * @psalm-mutation-free
2411
     *
2412
     * @return bool
2413
     */
2414
    public function isUrl(bool $disallow_localhost = false): bool
2415
    {
UNCOV
2416
        return $this->utf8::is_url($this->str, $disallow_localhost);
×
2417
    }
2418

2419
    /**
2420
     * Check if the string is UTF-16.
2421
     *
2422
     * @psalm-mutation-free
2423
     *
2424
     * @return false|int
2425
     *                   <strong>false</strong> if is't not UTF-16,<br>
2426
     *                   <strong>1</strong> for UTF-16LE,<br>
2427
     *                   <strong>2</strong> for UTF-16BE
2428
     */
2429
    public function isUtf16()
2430
    {
UNCOV
2431
        return $this->utf8::is_utf16($this->str);
×
2432
    }
2433

2434
    /**
2435
     * Check if the string is UTF-32.
2436
     *
2437
     * @psalm-mutation-free
2438
     *
2439
     * @return false|int
2440
     *                   <strong>false</strong> if is't not UTF-32,<br>
2441
     *                   <strong>1</strong> for UTF-32LE,<br>
2442
     *                   <strong>2</strong> for UTF-32BE
2443
     */
2444
    public function isUtf32()
2445
    {
UNCOV
2446
        return $this->utf8::is_utf32($this->str);
×
2447
    }
2448

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

2469
    /**
2470
     * Returns true if the string contains only whitespace chars, false otherwise.
2471
     *
2472
     * EXAMPLE: <code>
2473
     * </code>
2474
     *
2475
     * @psalm-mutation-free
2476
     *
2477
     * @return bool
2478
     *              <p>Whether or not $str contains only whitespace characters.</p>
2479
     */
2480
    public function isWhitespace(): bool
2481
    {
2482
        return $this->isBlank();
30✔
2483
    }
2484

2485
    /**
2486
     * Convert the string to kebab-case.
2487
     *
2488
     * EXAMPLE: <code>
2489
     * </code>
2490
     *
2491
     * @psalm-mutation-free
2492
     *
2493
     * @return static
2494
     */
2495
    public function kebabCase(): self
2496
    {
2497
        $words = \array_map(
4✔
2498
            static function (self $word) {
4✔
2499
                return $word->toLowerCase();
4✔
2500
            },
4✔
2501
            $this->words('', true)
4✔
2502
        );
4✔
2503

2504
        return new static(\implode('-', $words), $this->encoding);
4✔
2505
    }
2506

2507
    /**
2508
     * Returns the last $n characters of the string.
2509
     *
2510
     * EXAMPLE: <code>
2511
     * s('fòôbàř')->last(3); // 'bàř'
2512
     * </code>
2513
     *
2514
     * @param int $n <p>Number of characters to retrieve from the end.</p>
2515
     *
2516
     * @psalm-mutation-free
2517
     *
2518
     * @return static
2519
     *                <p>Object with its $str being the last $n chars.</p>
2520
     */
2521
    public function last(int $n): self
2522
    {
2523
        return static::create(
36✔
2524
            $this->utf8::str_last_char(
36✔
2525
                $this->str,
36✔
2526
                $n,
36✔
2527
                $this->encoding
36✔
2528
            ),
36✔
2529
            $this->encoding
36✔
2530
        );
36✔
2531
    }
2532

2533
    /**
2534
     * Gets the substring after (or before via "$beforeNeedle") the last occurrence of the "$needle".
2535
     * If no match is found returns new empty Stringy object.
2536
     *
2537
     * EXAMPLE: <code>
2538
     * </code>
2539
     *
2540
     * @param string $needle       <p>The string to look for.</p>
2541
     * @param bool   $beforeNeedle [optional] <p>Default: false</p>
2542
     *
2543
     * @psalm-mutation-free
2544
     *
2545
     * @return static
2546
     */
2547
    public function lastSubstringOf(string $needle, bool $beforeNeedle = false): self
2548
    {
2549
        return static::create(
5✔
2550
            $this->utf8::str_substr_last(
5✔
2551
                $this->str,
5✔
2552
                $needle,
5✔
2553
                $beforeNeedle,
5✔
2554
                $this->encoding
5✔
2555
            ),
5✔
2556
            $this->encoding
5✔
2557
        );
5✔
2558
    }
2559

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

2587
    /**
2588
     * Returns the length of the string.
2589
     *
2590
     * EXAMPLE: <code>
2591
     * s('fòôbàř')->length(); // 6
2592
     * </code>
2593
     *
2594
     * @psalm-mutation-free
2595
     *
2596
     * @return int
2597
     *             <p>The number of characters in $str given the encoding.</p>
2598
     */
2599
    public function length(): int
2600
    {
2601
        return (int) $this->utf8::strlen($this->str, $this->encoding);
30✔
2602
    }
2603

2604
    /**
2605
     * Line-Wrap the string after $limit, but also after the next word.
2606
     *
2607
     * EXAMPLE: <code>
2608
     * </code>
2609
     *
2610
     * @param int         $limit           [optional] <p>The column width.</p>
2611
     * @param string      $break           [optional] <p>The line is broken using the optional break parameter.</p>
2612
     * @param bool        $add_final_break [optional] <p>
2613
     *                                     If this flag is true, then the method will add a $break at the end
2614
     *                                     of the result string.
2615
     *                                     </p>
2616
     * @param string|null $delimiter       [optional] <p>
2617
     *                                     You can change the default behavior, where we split the string by newline.
2618
     *                                     </p>
2619
     *
2620
     * @psalm-mutation-free
2621
     *
2622
     * @return static
2623
     */
2624
    public function lineWrap(
2625
        int $limit,
2626
        string $break = "\n",
2627
        bool $add_final_break = true,
2628
        ?string $delimiter = null
2629
    ): self {
2630
        if ($limit <= 0) {
5✔
2631
            return static::create('', $this->encoding);
1✔
2632
        }
2633

2634
        $delimiter = $delimiter === '' ? null : $delimiter;
4✔
2635

2636
        return static::create(
4✔
2637
            $this->utf8::wordwrap_per_line(
4✔
2638
                $this->str,
4✔
2639
                $limit,
4✔
2640
                $break,
4✔
2641
                true,
4✔
2642
                $add_final_break,
4✔
2643
                $delimiter
4✔
2644
            ),
4✔
2645
            $this->encoding
4✔
2646
        );
4✔
2647
    }
2648

2649
    /**
2650
     * Line-Wrap the string after $limit, but also after the next word.
2651
     *
2652
     * EXAMPLE: <code>
2653
     * </code>
2654
     *
2655
     * @param int         $limit           [optional] <p>The column width.</p>
2656
     * @param string      $break           [optional] <p>The line is broken using the optional break parameter.</p>
2657
     * @param bool        $add_final_break [optional] <p>
2658
     *                                     If this flag is true, then the method will add a $break at the end
2659
     *                                     of the result string.
2660
     *                                     </p>
2661
     * @param string|null $delimiter       [optional] <p>
2662
     *                                     You can change the default behavior, where we split the string by newline.
2663
     *                                     </p>
2664
     *
2665
     * @psalm-mutation-free
2666
     *
2667
     * @return static
2668
     */
2669
    public function lineWrapAfterWord(
2670
        int $limit,
2671
        string $break = "\n",
2672
        bool $add_final_break = true,
2673
        ?string $delimiter = null
2674
    ): self {
2675
        if ($limit <= 0) {
8✔
2676
            return static::create('', $this->encoding);
2✔
2677
        }
2678

2679
        $delimiter = $delimiter === '' ? null : $delimiter;
6✔
2680

2681
        return static::create(
6✔
2682
            $this->utf8::wordwrap_per_line(
6✔
2683
                $this->str,
6✔
2684
                $limit,
6✔
2685
                $break,
6✔
2686
                false,
6✔
2687
                $add_final_break,
6✔
2688
                $delimiter
6✔
2689
            ),
6✔
2690
            $this->encoding
6✔
2691
        );
6✔
2692
    }
2693

2694
    /**
2695
     * Splits on newlines and carriage returns, returning an array of Stringy
2696
     * objects corresponding to the lines in the string.
2697
     *
2698
     * EXAMPLE: <code>
2699
     * s("fòô\r\nbàř\n")->lines(); // ['fòô', 'bàř', '']
2700
     * </code>
2701
     *
2702
     * @psalm-mutation-free
2703
     *
2704
     * @return static[]
2705
     *                  <p>An array of Stringy objects.</p>
2706
     *
2707
     * @phpstan-return array<int,static>
2708
     */
2709
    public function lines(): array
2710
    {
2711
        $strings = $this->utf8::str_to_lines($this->str);
52✔
2712
        /** @noinspection AlterInForeachInspection */
2713
        foreach ($strings as &$str) {
52✔
2714
            $str = static::create($str, $this->encoding);
52✔
2715
        }
2716

2717
        /** @noinspection PhpSillyAssignmentInspection */
2718
        /** @var static[] $strings */
2719
        $strings = $strings;
52✔
2720

2721
        return $strings;
52✔
2722
    }
2723

2724
    /**
2725
     * Splits on newlines and carriage returns, returning an array of Stringy
2726
     * objects corresponding to the lines in the string.
2727
     *
2728
     * EXAMPLE: <code>
2729
     * </code>
2730
     *
2731
     * @psalm-mutation-free
2732
     *
2733
     * @return CollectionStringy|static[]
2734
     *                                    <p>An collection of Stringy objects.</p>
2735
     *
2736
     * @phpstan-return CollectionStringy<int,static>
2737
     */
2738
    public function linesCollection(): CollectionStringy
2739
    {
2740
        /**
2741
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to the collection class
2742
         */
2743
        return CollectionStringy::create(
34✔
2744
            $this->lines()
34✔
2745
        );
34✔
2746
    }
2747

2748
    /**
2749
     * Returns the longest common prefix between the string and $otherStr.
2750
     *
2751
     * EXAMPLE: <code>
2752
     * s('foobar')->longestCommonPrefix('foobaz'); // 'fooba'
2753
     * </code>
2754
     *
2755
     * @param string $otherStr <p>Second string for comparison.</p>
2756
     *
2757
     * @psalm-mutation-free
2758
     *
2759
     * @return static
2760
     *                <p>Object with its $str being the longest common prefix.</p>
2761
     */
2762
    public function longestCommonPrefix(string $otherStr): self
2763
    {
2764
        return static::create(
30✔
2765
            $this->utf8::str_longest_common_prefix(
30✔
2766
                $this->str,
30✔
2767
                $otherStr,
30✔
2768
                $this->encoding
30✔
2769
            ),
30✔
2770
            $this->encoding
30✔
2771
        );
30✔
2772
    }
2773

2774
    /**
2775
     * Returns the longest common substring between the string and $otherStr.
2776
     * In the case of ties, it returns that which occurs first.
2777
     *
2778
     * EXAMPLE: <code>
2779
     * s('foobar')->longestCommonSubstring('boofar'); // 'oo'
2780
     * </code>
2781
     *
2782
     * @param string $otherStr <p>Second string for comparison.</p>
2783
     *
2784
     * @psalm-mutation-free
2785
     *
2786
     * @return static
2787
     *                <p>Object with its $str being the longest common substring.</p>
2788
     */
2789
    public function longestCommonSubstring(string $otherStr): self
2790
    {
2791
        return static::create(
30✔
2792
            $this->utf8::str_longest_common_substring(
30✔
2793
                $this->str,
30✔
2794
                $otherStr,
30✔
2795
                $this->encoding
30✔
2796
            ),
30✔
2797
            $this->encoding
30✔
2798
        );
30✔
2799
    }
2800

2801
    /**
2802
     * Returns the longest common suffix between the string and $otherStr.
2803
     *
2804
     * EXAMPLE: <code>
2805
     * s('fòôbàř')->longestCommonSuffix('fòrbàř'); // 'bàř'
2806
     * </code>
2807
     *
2808
     * @param string $otherStr <p>Second string for comparison.</p>
2809
     *
2810
     * @psalm-mutation-free
2811
     *
2812
     * @return static
2813
     *                <p>Object with its $str being the longest common suffix.</p>
2814
     */
2815
    public function longestCommonSuffix(string $otherStr): self
2816
    {
2817
        return static::create(
30✔
2818
            $this->utf8::str_longest_common_suffix(
30✔
2819
                $this->str,
30✔
2820
                $otherStr,
30✔
2821
                $this->encoding
30✔
2822
            ),
30✔
2823
            $this->encoding
30✔
2824
        );
30✔
2825
    }
2826

2827
    /**
2828
     * Converts the first character of the string to lower case.
2829
     *
2830
     * EXAMPLE: <code>
2831
     * s('Σ Foo')->lowerCaseFirst(); // 'σ Foo'
2832
     * </code>
2833
     *
2834
     * @psalm-mutation-free
2835
     *
2836
     * @return static
2837
     *                <p>Object with the first character of $str being lower case.</p>
2838
     */
2839
    public function lowerCaseFirst(): self
2840
    {
2841
        return static::create(
16✔
2842
            $this->utf8::lcfirst($this->str, $this->encoding),
16✔
2843
            $this->encoding
16✔
2844
        );
16✔
2845
    }
2846

2847
    /**
2848
     * Determine if the string matches another string regardless of case.
2849
     * Alias for isEqualsCaseInsensitive()
2850
     *
2851
     * EXAMPLE: <code>
2852
     * </code>
2853
     *
2854
     * @psalm-mutation-free
2855
     *
2856
     * @param string|Stringy ...$str
2857
     *                               <p>The string to compare against.</p>
2858
     *
2859
     * @psalm-mutation-free
2860
     *
2861
     * @return bool
2862
     */
2863
    public function matchCaseInsensitive(...$str): bool
2864
    {
2865
        return $this->isEqualsCaseInsensitive(...$str);
3✔
2866
    }
2867

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

2889
    /**
2890
     * Create a md5 hash from the current string.
2891
     *
2892
     * @psalm-mutation-free
2893
     *
2894
     * @return static
2895
     */
2896
    public function md5(): self
2897
    {
2898
        return static::create($this->hash('md5'), $this->encoding);
2✔
2899
    }
2900

2901
    /**
2902
     * Replace all breaks [<br> | \r\n | \r | \n | ...] into "<br>".
2903
     *
2904
     * EXAMPLE: <code>
2905
     * </code>
2906
     *
2907
     * @return static
2908
     */
2909
    public function newLineToHtmlBreak(): self
2910
    {
2911
        return $this->removeHtmlBreak('<br>');
1✔
2912
    }
2913

2914
    /**
2915
     * Get every nth character of the string.
2916
     *
2917
     * EXAMPLE: <code>
2918
     * </code>
2919
     *
2920
     * @param int $step   <p>The number of characters to step.</p>
2921
     * @param int $offset [optional] <p>The string offset to start at.</p>
2922
     *
2923
     * @psalm-mutation-free
2924
     *
2925
     * @return static
2926
     */
2927
    public function nth(int $step, int $offset = 0): self
2928
    {
2929
        $length = $step - 1;
4✔
2930
        $substring = $this->substr($offset)->toString();
4✔
2931

2932
        if ($substring === '') {
4✔
UNCOV
2933
            return new static('', $this->encoding);
×
2934
        }
2935

2936
        \preg_match_all(
4✔
2937
            "/(?:^|(?:.|\p{L}|\w){" . $length . "})(.|\p{L}|\w)/u",
4✔
2938
            $substring,
4✔
2939
            $matches
4✔
2940
        );
4✔
2941

2942
        return new static(\implode('', $matches[1] ?? []), $this->encoding);
4✔
2943
    }
2944

2945
    /**
2946
     * Returns the integer value of the current string.
2947
     *
2948
     * EXAMPLE: <code>
2949
     * s('foo1 ba2r')->extractIntegers(); // '12'
2950
     * </code>
2951
     *
2952
     * @psalm-mutation-free
2953
     *
2954
     * @return static
2955
     */
2956
    public function extractIntegers(): self
2957
    {
2958
        \preg_match_all('/(?<integers>\d+)/', $this->str, $matches);
1✔
2959

2960
        return static::create(
1✔
2961
            \implode('', $matches['integers']),
1✔
2962
            $this->encoding
1✔
2963
        );
1✔
2964
    }
2965

2966
    /**
2967
     * Returns the special chars of the current string.
2968
     *
2969
     * EXAMPLE: <code>
2970
     * s('foo1 ba2!r')->extractSpecialCharacters(); // '!'
2971
     * </code>
2972
     *
2973
     * @psalm-mutation-free
2974
     *
2975
     * @return static
2976
     */
2977
    public function extractSpecialCharacters(): self
2978
    {
2979
        // no letter, no digit, no space
2980
        \preg_match_all('/[^\p{L}0-9\s]/u', $this->str, $matches);
1✔
2981

2982
        return static::create(
1✔
2983
            \implode('', $matches[0]),
1✔
2984
            $this->encoding
1✔
2985
        );
1✔
2986
    }
2987

2988
    /**
2989
     * Returns whether or not a character exists at an index. Offsets may be
2990
     * negative to count from the last character in the string. Implements
2991
     * part of the ArrayAccess interface.
2992
     *
2993
     * EXAMPLE: <code>
2994
     * </code>
2995
     *
2996
     * @param int $offset <p>The index to check.</p>
2997
     *
2998
     * @psalm-mutation-free
2999
     *
3000
     * @return bool
3001
     *              <p>Whether or not the index exists.</p>
3002
     */
3003
    public function offsetExists($offset): bool
3004
    {
3005
        return $this->utf8::str_offset_exists(
18✔
3006
            $this->str,
18✔
3007
            $offset,
18✔
3008
            $this->encoding
18✔
3009
        );
18✔
3010
    }
3011

3012
    /**
3013
     * Returns the character at the given index. Offsets may be negative to
3014
     * count from the last character in the string. Implements part of the
3015
     * ArrayAccess interface, and throws an OutOfBoundsException if the index
3016
     * does not exist.
3017
     *
3018
     * EXAMPLE: <code>
3019
     * </code>
3020
     *
3021
     * @param int $offset <p>The <strong>index</strong> from which to retrieve the char.</p>
3022
     *
3023
     * @throws \OutOfBoundsException
3024
     *                               <p>If the positive or negative offset does not exist.</p>
3025
     *
3026
     * @return string
3027
     *                <p>The character at the specified index.</p>
3028
     *
3029
     * @psalm-mutation-free
3030
     */
3031
    public function offsetGet($offset): string
3032
    {
3033
        $length = $this->length();
11✔
3034

3035
        if (
3036
            ($offset >= 0 && $length <= $offset)
11✔
3037
            ||
3038
            $length < \abs($offset)
11✔
3039
        ) {
3040
            throw new \OutOfBoundsException('No character exists at the index');
6✔
3041
        }
3042

3043
        return (string) $this->utf8::substr($this->str, $offset, 1, $this->encoding);
5✔
3044
    }
3045

3046
    /**
3047
     * Implements part of the ArrayAccess interface, but throws an exception
3048
     * when called. This maintains the immutability of Stringy objects.
3049
     *
3050
     * EXAMPLE: <code>
3051
     * </code>
3052
     *
3053
     * @param int   $offset <p>The index of the character.</p>
3054
     * @param mixed $value  <p>Value to set.</p>
3055
     *
3056
     * @throws \Exception
3057
     *                    <p>When called.</p>
3058
     *
3059
     * @return void
3060
     */
3061
    public function offsetSet($offset, $value): void
3062
    {
3063
        // Stringy is immutable, cannot directly set char
3064
        throw new \Exception('Stringy object is immutable, cannot modify char');
3✔
3065
    }
3066

3067
    /**
3068
     * Implements part of the ArrayAccess interface, but throws an exception
3069
     * when called. This maintains the immutability of Stringy objects.
3070
     *
3071
     * EXAMPLE: <code>
3072
     * </code>
3073
     *
3074
     * @param int $offset <p>The index of the character.</p>
3075
     *
3076
     * @throws \Exception
3077
     *                    <p>When called.</p>
3078
     *
3079
     * @return void
3080
     */
3081
    public function offsetUnset($offset): void
3082
    {
3083
        // Don't allow directly modifying the string
3084
        throw new \Exception('Stringy object is immutable, cannot unset char');
3✔
3085
    }
3086

3087
    /**
3088
     * Pads the string to a given length with $padStr. If length is less than
3089
     * or equal to the length of the string, no padding takes places. The
3090
     * default string used for padding is a space, and the default type (one of
3091
     * 'left', 'right', 'both') is 'right'. Throws an InvalidArgumentException
3092
     * if $padType isn't one of those 3 values.
3093
     *
3094
     * EXAMPLE: <code>
3095
     * s('fòôbàř')->pad(9, '-/', 'left'); // '-/-fòôbàř'
3096
     * </code>
3097
     *
3098
     * @param int    $length  <p>Desired string length after padding.</p>
3099
     * @param string $padStr  [optional] <p>String used to pad, defaults to space. Default: ' '</p>
3100
     * @param string $padType [optional] <p>One of 'left', 'right', 'both'. Default: 'right'</p>
3101
     *
3102
     * @throws \InvalidArgumentException
3103
     *                                   <p>If $padType isn't one of 'right', 'left' or 'both'.</p>
3104
     *
3105
     * @return static
3106
     *                <p>Object with a padded $str.</p>
3107
     *
3108
     * @psalm-mutation-free
3109
     */
3110
    public function pad(int $length, string $padStr = ' ', string $padType = 'right'): self
3111
    {
3112
        return static::create(
39✔
3113
            $this->utf8::str_pad(
39✔
3114
                $this->str,
39✔
3115
                $length,
39✔
3116
                $padStr,
39✔
3117
                $padType,
39✔
3118
                $this->encoding
39✔
3119
            )
39✔
3120
        );
39✔
3121
    }
3122

3123
    /**
3124
     * Returns a new string of a given length such that both sides of the
3125
     * string are padded. Alias for pad() with a $padType of 'both'.
3126
     *
3127
     * EXAMPLE: <code>
3128
     * s('foo bar')->padBoth(9, ' '); // ' foo bar '
3129
     * </code>
3130
     *
3131
     * @param int    $length <p>Desired string length after padding.</p>
3132
     * @param string $padStr [optional] <p>String used to pad, defaults to space. Default: ' '</p>
3133
     *
3134
     * @psalm-mutation-free
3135
     *
3136
     * @return static
3137
     *                <p>String with padding applied.</p>
3138
     */
3139
    public function padBoth(int $length, string $padStr = ' '): self
3140
    {
3141
        return static::create(
33✔
3142
            $this->utf8::str_pad_both(
33✔
3143
                $this->str,
33✔
3144
                $length,
33✔
3145
                $padStr,
33✔
3146
                $this->encoding
33✔
3147
            )
33✔
3148
        );
33✔
3149
    }
3150

3151
    /**
3152
     * Returns a new string of a given length such that the beginning of the
3153
     * string is padded. Alias for pad() with a $padType of 'left'.
3154
     *
3155
     * EXAMPLE: <code>
3156
     * s('foo bar')->padLeft(9, ' '); // '  foo bar'
3157
     * </code>
3158
     *
3159
     * @param int    $length <p>Desired string length after padding.</p>
3160
     * @param string $padStr [optional] <p>String used to pad, defaults to space. Default: ' '</p>
3161
     *
3162
     * @psalm-mutation-free
3163
     *
3164
     * @return static
3165
     *                <p>String with left padding.</p>
3166
     */
3167
    public function padLeft(int $length, string $padStr = ' '): self
3168
    {
3169
        return static::create(
21✔
3170
            $this->utf8::str_pad_left(
21✔
3171
                $this->str,
21✔
3172
                $length,
21✔
3173
                $padStr,
21✔
3174
                $this->encoding
21✔
3175
            )
21✔
3176
        );
21✔
3177
    }
3178

3179
    /**
3180
     * Returns a new string of a given length such that the end of the string
3181
     * is padded. Alias for pad() with a $padType of 'right'.
3182
     *
3183
     * EXAMPLE: <code>
3184
     * s('foo bar')->padRight(10, '_*'); // 'foo bar_*_'
3185
     * </code>
3186
     *
3187
     * @param int    $length <p>Desired string length after padding.</p>
3188
     * @param string $padStr [optional] <p>String used to pad, defaults to space. Default: ' '</p>
3189
     *
3190
     * @psalm-mutation-free
3191
     *
3192
     * @return static
3193
     *                <p>String with right padding.</p>
3194
     */
3195
    public function padRight(int $length, string $padStr = ' '): self
3196
    {
3197
        return static::create(
21✔
3198
            $this->utf8::str_pad_right(
21✔
3199
                $this->str,
21✔
3200
                $length,
21✔
3201
                $padStr,
21✔
3202
                $this->encoding
21✔
3203
            )
21✔
3204
        );
21✔
3205
    }
3206

3207
    /**
3208
     * Convert the string to PascalCase.
3209
     * Alias for studlyCase()
3210
     *
3211
     * EXAMPLE: <code>
3212
     * </code>
3213
     *
3214
     * @psalm-mutation-free
3215
     *
3216
     * @return static
3217
     */
3218
    public function pascalCase(): self
3219
    {
3220
        return $this->studlyCase();
3✔
3221
    }
3222

3223
    /**
3224
     * Returns a new string starting with $prefix.
3225
     *
3226
     * EXAMPLE: <code>
3227
     * s('bàř')->prepend('fòô'); // 'fòôbàř'
3228
     * </code>
3229
     *
3230
     * @param string ...$prefix <p>The string to append.</p>
3231
     *
3232
     * @psalm-mutation-free
3233
     *
3234
     * @return static
3235
     *                <p>Object with appended $prefix.</p>
3236
     */
3237
    public function prepend(string ...$prefix): self
3238
    {
3239
        if (\count($prefix) <= 1) {
8✔
3240
            $prefix = $prefix[0];
6✔
3241
        } else {
3242
            $prefix = \implode('', $prefix);
2✔
3243
        }
3244

3245
        return static::create($prefix . $this->str, $this->encoding);
8✔
3246
    }
3247

3248
    /**
3249
     * Returns a new string starting with $prefix.
3250
     *
3251
     * EXAMPLE: <code>
3252
     * </code>
3253
     *
3254
     * @param CollectionStringy|static ...$prefix <p>The Stringy objects to append.</p>
3255
     *
3256
     * @phpstan-param CollectionStringy<int,static>|static ...$prefix
3257
     *
3258
     * @psalm-mutation-free
3259
     *
3260
     * @return static
3261
     *                <p>Object with appended $prefix.</p>
3262
     */
3263
    public function prependStringy(...$prefix): self
3264
    {
3265
        $prefixStr = '';
2✔
3266
        foreach ($prefix as $prefixTmp) {
2✔
3267
            if ($prefixTmp instanceof CollectionStringy) {
2✔
3268
                $prefixStr .= $prefixTmp->implode('');
2✔
3269
            } else {
3270
                $prefixStr .= $prefixTmp->toString();
2✔
3271
            }
3272
        }
3273

3274
        return static::create($prefixStr . $this->str, $this->encoding);
2✔
3275
    }
3276

3277
    /**
3278
     * Replaces all occurrences of $pattern in $str by $replacement.
3279
     *
3280
     * EXAMPLE: <code>
3281
     * s('fòô ')->regexReplace('f[òô]+\s', 'bàř'); // 'bàř'
3282
     * s('fò')->regexReplace('(ò)', '\\1ô'); // 'fòô'
3283
     * </code>
3284
     *
3285
     * @param string $pattern     <p>The regular expression pattern.</p>
3286
     * @param string $replacement <p>The string to replace with.</p>
3287
     * @param string $options     [optional] <p>Matching conditions to be used.</p>
3288
     * @param string $delimiter   [optional] <p>Delimiter the the regex. Default: '/'</p>
3289
     *
3290
     * @psalm-mutation-free
3291
     *
3292
     * @return static
3293
     *                <p>Object with the result2ing $str after the replacements.</p>
3294
     */
3295
    public function regexReplace(
3296
        string $pattern,
3297
        string $replacement,
3298
        string $options = '',
3299
        string $delimiter = '/'
3300
    ): self {
3301
        return static::create(
29✔
3302
            $this->utf8::regex_replace(
29✔
3303
                $this->str,
29✔
3304
                $pattern,
29✔
3305
                $replacement,
29✔
3306
                $options,
29✔
3307
                $delimiter
29✔
3308
            ),
29✔
3309
            $this->encoding
29✔
3310
        );
29✔
3311
    }
3312

3313
    /**
3314
     * Remove html via "strip_tags()" from the string.
3315
     *
3316
     * EXAMPLE: <code>
3317
     * s('řàb <ô>òf\', ô<br/>foo <a href="#">lall</a>')->removeHtml('<br><br/>'); // 'řàb òf\', ô<br/>foo lall'
3318
     * </code>
3319
     *
3320
     * @param string $allowableTags [optional] <p>You can use the optional second parameter to specify tags which should
3321
     *                              not be stripped. Default: null
3322
     *                              </p>
3323
     *
3324
     * @psalm-mutation-free
3325
     *
3326
     * @return static
3327
     */
3328
    public function removeHtml(string $allowableTags = ''): self
3329
    {
3330
        return static::create(
12✔
3331
            $this->utf8::remove_html($this->str, $allowableTags),
12✔
3332
            $this->encoding
12✔
3333
        );
12✔
3334
    }
3335

3336
    /**
3337
     * Remove all breaks [<br> | \r\n | \r | \n | ...] from the string.
3338
     *
3339
     * EXAMPLE: <code>
3340
     * s('řàb <ô>òf\', ô<br/>foo <a href="#">lall</a>')->removeHtmlBreak(''); // 'řàb <ô>òf\', ô< foo <a href="#">lall</a>'
3341
     * </code>
3342
     *
3343
     * @param string $replacement [optional] <p>Default is a empty string.</p>
3344
     *
3345
     * @psalm-mutation-free
3346
     *
3347
     * @return static
3348
     */
3349
    public function removeHtmlBreak(string $replacement = ''): self
3350
    {
3351
        return static::create(
13✔
3352
            $this->utf8::remove_html_breaks($this->str, $replacement),
13✔
3353
            $this->encoding
13✔
3354
        );
13✔
3355
    }
3356

3357
    /**
3358
     * Returns a new string with the prefix $substring removed, if present.
3359
     *
3360
     * EXAMPLE: <code>
3361
     * s('fòôbàř')->removeLeft('fòô'); // 'bàř'
3362
     * </code>
3363
     *
3364
     * @param string $substring <p>The prefix to remove.</p>
3365
     *
3366
     * @psalm-mutation-free
3367
     *
3368
     * @return static
3369
     *                <p>Object having a $str without the prefix $substring.</p>
3370
     */
3371
    public function removeLeft(string $substring): self
3372
    {
3373
        return static::create(
36✔
3374
            $this->utf8::remove_left($this->str, $substring, $this->encoding),
36✔
3375
            $this->encoding
36✔
3376
        );
36✔
3377
    }
3378

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

3401
    /**
3402
     * Try to remove all XSS-attacks from the string.
3403
     *
3404
     * EXAMPLE: <code>
3405
     * 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 >'
3406
     * </code>
3407
     *
3408
     * @psalm-mutation-free
3409
     *
3410
     * @return static
3411
     */
3412
    public function removeXss(): self
3413
    {
3414
        /**
3415
         * @var AntiXSS|null
3416
         *
3417
         * @psalm-suppress ImpureStaticVariable
3418
         */
3419
        static $antiXss = null;
12✔
3420

3421
        if ($antiXss === null) {
12✔
3422
            $antiXss = new AntiXSS();
1✔
3423
        }
3424

3425
        /**
3426
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to the anti-xss class
3427
         */
3428
        $str = $antiXss->xss_clean($this->str);
12✔
3429

3430
        return static::create($str, $this->encoding);
12✔
3431
    }
3432

3433
    /**
3434
     * Returns a repeated string given a multiplier.
3435
     *
3436
     * EXAMPLE: <code>
3437
     * s('α')->repeat(3); // 'ααα'
3438
     * </code>
3439
     *
3440
     * @param int $multiplier <p>The number of times to repeat the string.</p>
3441
     *
3442
     * @psalm-mutation-free
3443
     *
3444
     * @return static
3445
     *                <p>Object with a repeated str.</p>
3446
     */
3447
    public function repeat(int $multiplier): self
3448
    {
3449
        return static::create(
21✔
3450
            \str_repeat($this->str, $multiplier),
21✔
3451
            $this->encoding
21✔
3452
        );
21✔
3453
    }
3454

3455
    /**
3456
     * Replaces all occurrences of $search in $str by $replacement.
3457
     *
3458
     * EXAMPLE: <code>
3459
     * s('fòô bàř fòô bàř')->replace('fòô ', ''); // 'bàř bàř'
3460
     * </code>
3461
     *
3462
     * @param string $search        <p>The needle to search for.</p>
3463
     * @param string $replacement   <p>The string to replace with.</p>
3464
     * @param bool   $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
3465
     *
3466
     * @psalm-mutation-free
3467
     *
3468
     * @return static
3469
     *                <p>Object with the resulting $str after the replacements.</p>
3470
     */
3471
    public function replace(string $search, string $replacement, bool $caseSensitive = true): self
3472
    {
3473
        if ($this->str === '' && $search === '') {
77✔
3474
            return static::create($replacement, $this->encoding);
10✔
3475
        }
3476

3477
        if ($caseSensitive) {
67✔
3478
            return static::create(
55✔
3479
                \str_replace($search, $replacement, $this->str),
55✔
3480
                $this->encoding
55✔
3481
            );
55✔
3482
        }
3483

3484
        return static::create(
12✔
3485
            $this->utf8::str_ireplace($search, $replacement, $this->str),
12✔
3486
            $this->encoding
12✔
3487
        );
12✔
3488
    }
3489

3490
    /**
3491
     * Replaces all occurrences of $search in $str by $replacement.
3492
     *
3493
     * EXAMPLE: <code>
3494
     * s('fòô bàř lall bàř')->replaceAll(['fòÔ ', 'lall'], '', false); // 'bàř bàř'
3495
     * </code>
3496
     *
3497
     * @param string[]        $search        <p>The elements to search for.</p>
3498
     * @param string|string[] $replacement   <p>The string to replace with.</p>
3499
     * @param bool            $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
3500
     *
3501
     * @psalm-mutation-free
3502
     *
3503
     * @return static
3504
     *                <p>Object with the resulting $str after the replacements.</p>
3505
     */
3506
    public function replaceAll(array $search, $replacement, bool $caseSensitive = true): self
3507
    {
3508
        if ($caseSensitive) {
62✔
3509
            return static::create(
48✔
3510
                \str_replace($search, $replacement, $this->str),
48✔
3511
                $this->encoding
48✔
3512
            );
48✔
3513
        }
3514

3515
        return static::create(
14✔
3516
            $this->utf8::str_ireplace($search, $replacement, $this->str),
14✔
3517
            $this->encoding
14✔
3518
        );
14✔
3519
    }
3520

3521
    /**
3522
     * Replaces all occurrences of $search from the beginning of string with $replacement.
3523
     *
3524
     * EXAMPLE: <code>
3525
     * s('fòô bàř fòô bàř')->replaceBeginning('fòô', ''); // ' bàř bàř'
3526
     * </code>
3527
     *
3528
     * @param string $search      <p>The string to search for.</p>
3529
     * @param string $replacement <p>The replacement.</p>
3530
     *
3531
     * @psalm-mutation-free
3532
     *
3533
     * @return static
3534
     *                <p>Object with the resulting $str after the replacements.</p>
3535
     */
3536
    public function replaceBeginning(string $search, string $replacement): self
3537
    {
3538
        return static::create(
32✔
3539
            $this->utf8::str_replace_beginning($this->str, $search, $replacement),
32✔
3540
            $this->encoding
32✔
3541
        );
32✔
3542
    }
3543

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

3567
    /**
3568
     * Replaces first occurrences of $search from the beginning of string with $replacement.
3569
     *
3570
     * EXAMPLE: <code>
3571
     * </code>
3572
     *
3573
     * @param string $search      <p>The string to search for.</p>
3574
     * @param string $replacement <p>The replacement.</p>
3575
     *
3576
     * @psalm-mutation-free
3577
     *
3578
     * @return static
3579
     *                <p>Object with the resulting $str after the replacements.</p>
3580
     */
3581
    public function replaceFirst(string $search, string $replacement): self
3582
    {
3583
        return static::create(
32✔
3584
            $this->utf8::str_replace_first($search, $replacement, $this->str),
32✔
3585
            $this->encoding
32✔
3586
        );
32✔
3587
    }
3588

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

3611
    /**
3612
     * Returns a reversed string. A multibyte version of strrev().
3613
     *
3614
     * EXAMPLE: <code>
3615
     * s('fòôbàř')->reverse(); // 'řàbôòf'
3616
     * </code>
3617
     *
3618
     * @psalm-mutation-free
3619
     *
3620
     * @return static
3621
     *                <p>Object with a reversed $str.</p>
3622
     */
3623
    public function reverse(): self
3624
    {
3625
        return static::create($this->utf8::strrev($this->str), $this->encoding);
15✔
3626
    }
3627

3628
    /**
3629
     * Truncates the string to a given length, while ensuring that it does not
3630
     * split words. If $substring is provided, and truncating occurs, the
3631
     * string is further truncated so that the substring may be appended without
3632
     * exceeding the desired length.
3633
     *
3634
     * EXAMPLE: <code>
3635
     * s('What are your plans today?')->safeTruncate(22, '...'); // 'What are your plans...'
3636
     * </code>
3637
     *
3638
     * @param int    $length                          <p>Desired length of the truncated string.</p>
3639
     * @param string $substring                       [optional] <p>The substring to append if it can fit. Default: ''</p>
3640
     * @param bool   $ignoreDoNotSplitWordsForOneWord
3641
     *
3642
     * @psalm-mutation-free
3643
     *
3644
     * @return static
3645
     *                <p>Object with the resulting $str after truncating.</p>
3646
     */
3647
    public function safeTruncate(
3648
        int $length,
3649
        string $substring = '',
3650
        bool $ignoreDoNotSplitWordsForOneWord = true
3651
    ): self {
3652
        return static::create(
68✔
3653
            $this->utf8::str_truncate_safe(
68✔
3654
                $this->str,
68✔
3655
                $length,
68✔
3656
                $substring,
68✔
3657
                $this->encoding,
68✔
3658
                $ignoreDoNotSplitWordsForOneWord
68✔
3659
            ),
68✔
3660
            $this->encoding
68✔
3661
        );
68✔
3662
    }
3663

3664
    /**
3665
     * Set the internal character encoding.
3666
     *
3667
     * EXAMPLE: <code>
3668
     * </code>
3669
     *
3670
     * @param string $new_encoding <p>The desired character encoding.</p>
3671
     *
3672
     * @psalm-mutation-free
3673
     *
3674
     * @return static
3675
     */
3676
    public function setInternalEncoding(string $new_encoding): self
3677
    {
3678
        return new static($this->str, $new_encoding);
1✔
3679
    }
3680

3681
    /**
3682
     * Create a sha1 hash from the current string.
3683
     *
3684
     * EXAMPLE: <code>
3685
     * </code>
3686
     *
3687
     * @psalm-mutation-free
3688
     *
3689
     * @return static
3690
     */
3691
    public function sha1(): self
3692
    {
3693
        return static::create($this->hash('sha1'), $this->encoding);
2✔
3694
    }
3695

3696
    /**
3697
     * Create a sha256 hash from the current string.
3698
     *
3699
     * EXAMPLE: <code>
3700
     * </code>
3701
     *
3702
     * @psalm-mutation-free
3703
     *
3704
     * @return static
3705
     */
3706
    public function sha256(): self
3707
    {
3708
        return static::create($this->hash('sha256'), $this->encoding);
2✔
3709
    }
3710

3711
    /**
3712
     * Create a sha512 hash from the current string.
3713
     *
3714
     * EXAMPLE: <code>
3715
     * </code>
3716
     *
3717
     * @psalm-mutation-free
3718
     *
3719
     * @return static
3720
     */
3721
    public function sha512(): self
3722
    {
3723
        return static::create($this->hash('sha512'), $this->encoding);
2✔
3724
    }
3725

3726
    /**
3727
     * Shorten the string after $length, but also after the next word.
3728
     *
3729
     * EXAMPLE: <code>
3730
     * s('this is a test')->shortenAfterWord(2, '...'); // 'this...'
3731
     * </code>
3732
     *
3733
     * @param int    $length   <p>The given length.</p>
3734
     * @param string $strAddOn [optional] <p>Default: '…'</p>
3735
     *
3736
     * @psalm-mutation-free
3737
     *
3738
     * @return static
3739
     */
3740
    public function shortenAfterWord(int $length, string $strAddOn = '…'): self
3741
    {
3742
        if ($length <= 0) {
12✔
3743
            return static::create('', $this->encoding);
4✔
3744
        }
3745

3746
        return static::create(
8✔
3747
            $this->utf8::str_limit_after_word($this->str, $length, $strAddOn),
8✔
3748
            $this->encoding
8✔
3749
        );
8✔
3750
    }
3751

3752
    /**
3753
     * A multibyte string shuffle function. It returns a string with its
3754
     * characters in random order.
3755
     *
3756
     * EXAMPLE: <code>
3757
     * s('fòôbàř')->shuffle(); // 'àôřbòf'
3758
     * </code>
3759
     *
3760
     * @return static
3761
     *                <p>Object with a shuffled $str.</p>
3762
     */
3763
    public function shuffle(): self
3764
    {
3765
        return static::create($this->utf8::str_shuffle($this->str), $this->encoding);
9✔
3766
    }
3767

3768
    /**
3769
     * Calculate the similarity between two strings.
3770
     *
3771
     * EXAMPLE: <code>
3772
     * </code>
3773
     *
3774
     * @param string $str <p>The delimiting string.</p>
3775
     *
3776
     * @psalm-mutation-free
3777
     *
3778
     * @return float
3779
     */
3780
    public function similarity(string $str): float
3781
    {
3782
        \similar_text($this->str, $str, $percent);
3✔
3783

3784
        return $percent;
3✔
3785
    }
3786

3787
    /**
3788
     * Returns the substring beginning at $start, and up to, but not including
3789
     * the index specified by $end. If $end is omitted, the function extracts
3790
     * the remaining string. If $end is negative, it is computed from the end
3791
     * of the string.
3792
     *
3793
     * EXAMPLE: <code>
3794
     * s('fòôbàř')->slice(3, -1); // 'bà'
3795
     * </code>
3796
     *
3797
     * @param int $start <p>Initial index from which to begin extraction.</p>
3798
     * @param int $end   [optional] <p>Index at which to end extraction. Default: null</p>
3799
     *
3800
     * @psalm-mutation-free
3801
     *
3802
     * @return static
3803
     *                <p>Object with its $str being the extracted substring.</p>
3804
     */
3805
    public function slice(int $start, ?int $end = null): self
3806
    {
3807
        return static::create(
51✔
3808
            $this->utf8::str_slice($this->str, $start, $end, $this->encoding),
51✔
3809
            $this->encoding
51✔
3810
        );
51✔
3811
    }
3812

3813
    /**
3814
     * Converts the string into an URL slug. This includes replacing non-ASCII
3815
     * characters with their closest ASCII equivalents, removing remaining
3816
     * non-ASCII and non-alphanumeric characters, and replacing whitespace with
3817
     * $separator. The separator defaults to a single dash, and the string
3818
     * is also converted to lowercase. The language of the source string can
3819
     * also be supplied for language-specific transliteration.
3820
     *
3821
     * EXAMPLE: <code>
3822
     * s('Using strings like fòô bàř')->slugify(); // 'using-strings-like-foo-bar'
3823
     * </code>
3824
     *
3825
     * @param string                $separator             [optional] <p>The string used to replace whitespace.</p>
3826
     * @param string                $language              [optional] <p>Language of the source string.</p>
3827
     * @param array<string, string> $replacements          [optional] <p>A map of replaceable strings.</p>
3828
     * @param bool                  $replace_extra_symbols [optional]  <p>Add some more replacements e.g. "£" with "
3829
     *                                                     pound ".</p>
3830
     * @param bool                  $use_str_to_lower      [optional] <p>Use "string to lower" for the input.</p>
3831
     * @param bool                  $use_transliterate     [optional]  <p>Use ASCII::to_transliterate() for unknown
3832
     *                                                     chars.</p>
3833
     *
3834
     * @psalm-mutation-free
3835
     *
3836
     * @return static
3837
     *                <p>Object whose $str has been converted to an URL slug.</p>
3838
     *
3839
     * @phpstan-param ASCII::*_LANGUAGE_CODE $language
3840
     *
3841
     * @noinspection PhpTooManyParametersInspection
3842
     */
3843
    public function slugify(
3844
        string $separator = '-',
3845
        string $language = 'en',
3846
        array $replacements = [],
3847
        bool $replace_extra_symbols = true,
3848
        bool $use_str_to_lower = true,
3849
        bool $use_transliterate = false
3850
    ): self {
3851
        return static::create(
18✔
3852
            $this->ascii::to_slugify(
18✔
3853
                $this->str,
18✔
3854
                $separator,
18✔
3855
                $language,
18✔
3856
                $replacements,
18✔
3857
                $replace_extra_symbols,
18✔
3858
                $use_str_to_lower,
18✔
3859
                $use_transliterate
18✔
3860
            ),
18✔
3861
            $this->encoding
18✔
3862
        );
18✔
3863
    }
3864

3865
    /**
3866
     * Convert the string to snake_case.
3867
     *
3868
     * EXAMPLE: <code>
3869
     * </code>
3870
     *
3871
     * @psalm-mutation-free
3872
     *
3873
     * @return static
3874
     */
3875
    public function snakeCase(): self
3876
    {
3877
        $words = \array_map(
4✔
3878
            static function (self $word) {
4✔
3879
                return $word->toLowerCase();
4✔
3880
            },
4✔
3881
            $this->words('', true)
4✔
3882
        );
4✔
3883

3884
        return new static(\implode('_', $words), $this->encoding);
4✔
3885
    }
3886

3887
    /**
3888
     * Convert a string to snake_case.
3889
     *
3890
     * EXAMPLE: <code>
3891
     * s('foo1 Bar')->snakeize(); // 'foo_1_bar'
3892
     * </code>
3893
     *
3894
     * @psalm-mutation-free
3895
     *
3896
     * @return static
3897
     *                <p>Object with $str in snake_case.</p>
3898
     */
3899
    public function snakeize(): self
3900
    {
3901
        return static::create(
40✔
3902
            $this->utf8::str_snakeize($this->str, $this->encoding),
40✔
3903
            $this->encoding
40✔
3904
        );
40✔
3905
    }
3906

3907
    /**
3908
     * Wrap the string after the first whitespace character after a given number
3909
     * of characters.
3910
     *
3911
     * EXAMPLE: <code>
3912
     * </code>
3913
     *
3914
     * @param int    $width <p>Number of characters at which to wrap.</p>
3915
     * @param string $break [optional] <p>Character used to break the string. | Default "\n"</p>
3916
     *
3917
     * @psalm-mutation-free
3918
     *
3919
     * @return static
3920
     */
3921
    public function softWrap(int $width, string $break = "\n"): self
3922
    {
3923
        return $this->lineWrapAfterWord($width, $break, false);
2✔
3924
    }
3925

3926
    /**
3927
     * Splits the string with the provided regular expression, returning an
3928
     * array of Stringy objects. An optional integer $limit will truncate the
3929
     * results.
3930
     *
3931
     * EXAMPLE: <code>
3932
     * s('foo,bar,baz')->split(',', 2); // ['foo', 'bar']
3933
     * </code>
3934
     *
3935
     * @param string $pattern <p>The regex with which to split the string.</p>
3936
     * @param int    $limit   [optional] <p>Maximum number of results to return. Default: -1 === no
3937
     *                        limit</p>
3938
     *
3939
     * @psalm-mutation-free
3940
     *
3941
     * @return static[]
3942
     *                  <p>An array of Stringy objects.</p>
3943
     *
3944
     * @phpstan-return array<int,static>
3945
     */
3946
    public function split(string $pattern, ?int $limit = null): array
3947
    {
3948
        if ($this->str === '') {
53✔
UNCOV
3949
            return [];
×
3950
        }
3951

3952
        if ($limit === null) {
53✔
3953
            $array = $this->utf8::str_split_pattern($this->str, $pattern);
9✔
3954
        } else {
3955
            $array = $this->utf8::str_split_pattern($this->str, $pattern, $limit);
44✔
3956
        }
3957

3958
        foreach ($array as &$value) {
53✔
3959
            $value = static::create($value, $this->encoding);
47✔
3960
        }
3961

3962
        /** @noinspection PhpSillyAssignmentInspection */
3963
        /** @var static[] $array */
3964
        $array = $array;
53✔
3965

3966
        return $array;
53✔
3967
    }
3968

3969
    /**
3970
     * Splits the string with the provided regular expression, returning an
3971
     * collection of Stringy objects. An optional integer $limit will truncate the
3972
     * results.
3973
     *
3974
     * EXAMPLE: <code>
3975
     * </code>
3976
     *
3977
     * @param string $pattern <p>The regex with which to split the string.</p>
3978
     * @param int    $limit   [optional] <p>Maximum number of results to return. Default: -1 === no
3979
     *                        limit</p>
3980
     *
3981
     * @psalm-mutation-free
3982
     *
3983
     * @return CollectionStringy|static[]
3984
     *                                    <p>An collection of Stringy objects.</p>
3985
     *
3986
     * @phpstan-return CollectionStringy<int,static>
3987
     */
3988
    public function splitCollection(string $pattern, ?int $limit = null): CollectionStringy
3989
    {
3990
        /**
3991
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to the collection class
3992
         */
3993
        return CollectionStringy::create(
35✔
3994
            $this->split($pattern, $limit)
35✔
3995
        );
35✔
3996
    }
3997

3998
    /**
3999
     * Returns true if the string begins with $substring, false otherwise. By
4000
     * default, the comparison is case-sensitive, but can be made insensitive
4001
     * by setting $caseSensitive to false.
4002
     *
4003
     * EXAMPLE: <code>
4004
     * s('FÒÔbàřbaz')->startsWith('fòôbàř', false); // true
4005
     * </code>
4006
     *
4007
     * @param string $substring     <p>The substring to look for.</p>
4008
     * @param bool   $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
4009
     *
4010
     * @psalm-mutation-free
4011
     *
4012
     * @return bool
4013
     *              <p>Whether or not $str starts with $substring.</p>
4014
     */
4015
    public function startsWith(string $substring, bool $caseSensitive = true): bool
4016
    {
4017
        if ($caseSensitive) {
99✔
4018
            return $this->utf8::str_starts_with($this->str, $substring);
53✔
4019
        }
4020

4021
        return $this->utf8::str_istarts_with($this->str, $substring);
46✔
4022
    }
4023

4024
    /**
4025
     * Returns true if the string begins with any of $substrings, false otherwise.
4026
     * By default the comparison is case-sensitive, but can be made insensitive by
4027
     * setting $caseSensitive to false.
4028
     *
4029
     * EXAMPLE: <code>
4030
     * s('FÒÔbàřbaz')->startsWithAny(['fòô', 'bàř'], false); // true
4031
     * </code>
4032
     *
4033
     * @param string[] $substrings    <p>Substrings to look for.</p>
4034
     * @param bool     $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
4035
     *
4036
     * @psalm-mutation-free
4037
     *
4038
     * @return bool
4039
     *              <p>Whether or not $str starts with $substring.</p>
4040
     */
4041
    public function startsWithAny(array $substrings, bool $caseSensitive = true): bool
4042
    {
4043
        if ($caseSensitive) {
36✔
4044
            return $this->utf8::str_starts_with_any($this->str, $substrings);
24✔
4045
        }
4046

4047
        return $this->utf8::str_istarts_with_any($this->str, $substrings);
12✔
4048
    }
4049

4050
    /**
4051
     * Remove one or more strings from the string.
4052
     *
4053
     * EXAMPLE: <code>
4054
     * </code>
4055
     *
4056
     * @param string|string[] $search One or more strings to be removed
4057
     *
4058
     * @psalm-mutation-free
4059
     *
4060
     * @return static
4061
     */
4062
    public function strip($search): self
4063
    {
4064
        if (\is_array($search)) {
3✔
4065
            return $this->replaceAll($search, '');
1✔
4066
        }
4067

4068
        return $this->replace($search, '');
2✔
4069
    }
4070

4071
    /**
4072
     * Strip all whitespace characters. This includes tabs and newline characters,
4073
     * as well as multibyte whitespace such as the thin space and ideographic space.
4074
     *
4075
     * EXAMPLE: <code>
4076
     * s('   Ο     συγγραφέας  ')->stripWhitespace(); // 'Οσυγγραφέας'
4077
     * </code>
4078
     *
4079
     * @psalm-mutation-free
4080
     *
4081
     * @return static
4082
     */
4083
    public function stripWhitespace(): self
4084
    {
4085
        return static::create(
36✔
4086
            $this->utf8::strip_whitespace($this->str),
36✔
4087
            $this->encoding
36✔
4088
        );
36✔
4089
    }
4090

4091
    /**
4092
     * Remove css media-queries.
4093
     *
4094
     * EXAMPLE: <code>
4095
     * s('test @media (min-width:660px){ .des-cla #mv-tiles{width:480px} } test ')->stripeCssMediaQueries(); // 'test  test '
4096
     * </code>
4097
     *
4098
     * @psalm-mutation-free
4099
     *
4100
     * @return static
4101
     */
4102
    public function stripeCssMediaQueries(): self
4103
    {
4104
        return static::create(
2✔
4105
            $this->utf8::css_stripe_media_queries($this->str),
2✔
4106
            $this->encoding
2✔
4107
        );
2✔
4108
    }
4109

4110
    /**
4111
     * Remove empty html-tag.
4112
     *
4113
     * EXAMPLE: <code>
4114
     * s('foo<h1></h1>bar')->stripeEmptyHtmlTags(); // 'foobar'
4115
     * </code>
4116
     *
4117
     * @psalm-mutation-free
4118
     *
4119
     * @return static
4120
     */
4121
    public function stripeEmptyHtmlTags(): self
4122
    {
4123
        return static::create(
2✔
4124
            $this->utf8::html_stripe_empty_tags($this->str),
2✔
4125
            $this->encoding
2✔
4126
        );
2✔
4127
    }
4128

4129
    /**
4130
     * Convert the string to StudlyCase.
4131
     *
4132
     * EXAMPLE: <code>
4133
     * </code>
4134
     *
4135
     * @psalm-mutation-free
4136
     *
4137
     * @return static
4138
     */
4139
    public function studlyCase(): self
4140
    {
4141
        $words = \array_map(
6✔
4142
            static function (self $word) {
6✔
4143
                return $word->substr(0, 1)
6✔
4144
                    ->toUpperCase()
6✔
4145
                    ->appendStringy($word->substr(1));
6✔
4146
            },
6✔
4147
            $this->words('', true)
6✔
4148
        );
6✔
4149

4150
        return new static(\implode('', $words), $this->encoding);
6✔
4151
    }
4152

4153
    /**
4154
     * Returns the substring beginning at $start with the specified $length.
4155
     * It differs from the $this->utf8::substr() function in that providing a $length of
4156
     * null will return the rest of the string, rather than an empty string.
4157
     *
4158
     * EXAMPLE: <code>
4159
     * </code>
4160
     *
4161
     * @param int $start  <p>Position of the first character to use.</p>
4162
     * @param int $length [optional] <p>Maximum number of characters used. Default: null</p>
4163
     *
4164
     * @psalm-mutation-free
4165
     *
4166
     * @return static
4167
     *                <p>Object with its $str being the substring.</p>
4168
     */
4169
    public function substr(int $start, ?int $length = null): self
4170
    {
4171
        return static::create(
41✔
4172
            $this->utf8::substr(
41✔
4173
                $this->str,
41✔
4174
                $start,
41✔
4175
                $length,
41✔
4176
                $this->encoding
41✔
4177
            ),
41✔
4178
            $this->encoding
41✔
4179
        );
41✔
4180
    }
4181

4182
    /**
4183
     * Return part of the string.
4184
     * Alias for substr()
4185
     *
4186
     * EXAMPLE: <code>
4187
     * s('fòôbàř')->substring(2, 3); // 'ôbà'
4188
     * </code>
4189
     *
4190
     * @param int $start  <p>Starting position of the substring.</p>
4191
     * @param int $length [optional] <p>Length of substring.</p>
4192
     *
4193
     * @psalm-mutation-free
4194
     *
4195
     * @return static
4196
     */
4197
    public function substring(int $start, ?int $length = null): self
4198
    {
4199
        return $this->substr($start, $length);
4✔
4200
    }
4201

4202
    /**
4203
     * Gets the substring after (or before via "$beforeNeedle") the first occurrence of the "$needle".
4204
     * If no match is found returns new empty Stringy object.
4205
     *
4206
     * EXAMPLE: <code>
4207
     * </code>
4208
     *
4209
     * @param string $needle       <p>The string to look for.</p>
4210
     * @param bool   $beforeNeedle [optional] <p>Default: false</p>
4211
     *
4212
     * @psalm-mutation-free
4213
     *
4214
     * @return static
4215
     */
4216
    public function substringOf(string $needle, bool $beforeNeedle = false): self
4217
    {
4218
        return static::create(
5✔
4219
            $this->utf8::str_substr_first(
5✔
4220
                $this->str,
5✔
4221
                $needle,
5✔
4222
                $beforeNeedle,
5✔
4223
                $this->encoding
5✔
4224
            ),
5✔
4225
            $this->encoding
5✔
4226
        );
5✔
4227
    }
4228

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

4256
    /**
4257
     * Surrounds $str with the given substring.
4258
     *
4259
     * EXAMPLE: <code>
4260
     * s(' ͜ ')->surround('ʘ'); // 'ʘ ͜ ʘ'
4261
     * </code>
4262
     *
4263
     * @param string $substring <p>The substring to add to both sides.</P>
4264
     *
4265
     * @psalm-mutation-free
4266
     *
4267
     * @return static
4268
     *                <p>Object whose $str had the substring both prepended and appended.</p>
4269
     */
4270
    public function surround(string $substring): self
4271
    {
4272
        return static::create(
15✔
4273
            $substring . $this->str . $substring,
15✔
4274
            $this->encoding
15✔
4275
        );
15✔
4276
    }
4277

4278
    /**
4279
     * Returns a case swapped version of the string.
4280
     *
4281
     * EXAMPLE: <code>
4282
     * s('Ντανιλ')->swapCase(); // 'νΤΑΝΙΛ'
4283
     * </code>
4284
     *
4285
     * @psalm-mutation-free
4286
     *
4287
     * @return static
4288
     *                <p>Object whose $str has each character's case swapped.</P>
4289
     */
4290
    public function swapCase(): self
4291
    {
4292
        return static::create(
15✔
4293
            $this->utf8::swapCase($this->str, $this->encoding),
15✔
4294
            $this->encoding
15✔
4295
        );
15✔
4296
    }
4297

4298
    /**
4299
     * Returns a string with smart quotes, ellipsis characters, and dashes from
4300
     * Windows-1252 (commonly used in Word documents) replaced by their ASCII
4301
     * equivalents.
4302
     *
4303
     * EXAMPLE: <code>
4304
     * s('“I see…”')->tidy(); // '"I see..."'
4305
     * </code>
4306
     *
4307
     * @psalm-mutation-free
4308
     *
4309
     * @return static
4310
     *                <p>Object whose $str has those characters removed.</p>
4311
     */
4312
    public function tidy(): self
4313
    {
4314
        return static::create(
12✔
4315
            $this->ascii::normalize_msword($this->str),
12✔
4316
            $this->encoding
12✔
4317
        );
12✔
4318
    }
4319

4320
    /**
4321
     * Returns a trimmed string with the first letter of each word capitalized.
4322
     * Also accepts an array, $ignore, allowing you to list words not to be
4323
     * capitalized.
4324
     *
4325
     * EXAMPLE: <code>
4326
     * $ignore = ['at', 'by', 'for', 'in', 'of', 'on', 'out', 'to', 'the'];
4327
     * s('i like to watch television')->titleize($ignore); // 'I Like to Watch Television'
4328
     * </code>
4329
     *
4330
     * @param string[]|null $ignore            [optional] <p>An array of words not to capitalize or null.
4331
     *                                         Default: null</p>
4332
     * @param string|null   $word_define_chars [optional] <p>An string of chars that will be used as whitespace
4333
     *                                         separator === words.</p>
4334
     * @param string|null   $language          [optional] <p>Language of the source string.</p>
4335
     *
4336
     * @psalm-mutation-free
4337
     *
4338
     * @return static
4339
     *                <p>Object with a titleized $str.</p>
4340
     */
4341
    public function titleize(
4342
        ?array $ignore = null,
4343
        ?string $word_define_chars = null,
4344
        ?string $language = null
4345
    ): self {
4346
        return static::create(
25✔
4347
            $this->utf8::str_titleize(
25✔
4348
                $this->str,
25✔
4349
                $ignore,
25✔
4350
                $this->encoding,
25✔
4351
                false,
25✔
4352
                $language,
25✔
4353
                false,
25✔
4354
                true,
25✔
4355
                $word_define_chars
25✔
4356
            ),
25✔
4357
            $this->encoding
25✔
4358
        );
25✔
4359
    }
4360

4361
    /**
4362
     * Returns a trimmed string in proper title case: Also accepts an array, $ignore, allowing you to list words not to
4363
     * be capitalized.
4364
     *
4365
     * EXAMPLE: <code>
4366
     * </code>
4367
     *
4368
     * Adapted from John Gruber's script.
4369
     *
4370
     * @see https://gist.github.com/gruber/9f9e8650d68b13ce4d78
4371
     *
4372
     * @param string[] $ignore <p>An array of words not to capitalize.</p>
4373
     *
4374
     * @psalm-mutation-free
4375
     *
4376
     * @return static
4377
     *                <p>Object with a titleized $str</p>
4378
     */
4379
    public function titleizeForHumans(array $ignore = []): self
4380
    {
4381
        return static::create(
70✔
4382
            $this->utf8::str_titleize_for_humans(
70✔
4383
                $this->str,
70✔
4384
                $ignore,
70✔
4385
                $this->encoding
70✔
4386
            ),
70✔
4387
            $this->encoding
70✔
4388
        );
70✔
4389
    }
4390

4391
    /**
4392
     * Returns an ASCII version of the string. A set of non-ASCII characters are
4393
     * replaced with their closest ASCII counterparts, and the rest are removed
4394
     * by default. The language or locale of the source string can be supplied
4395
     * for language-specific transliteration in any of the following formats:
4396
     * en, en_GB, or en-GB. For example, passing "de" results in "äöü" mapping
4397
     * to "aeoeue" rather than "aou" as in other languages.
4398
     *
4399
     * EXAMPLE: <code>
4400
     * s('fòôbàř')->toAscii(); // 'foobar'
4401
     * </code>
4402
     *
4403
     * @param string $language          [optional] <p>Language of the source string.</p>
4404
     * @param bool   $removeUnsupported [optional] <p>Whether or not to remove the
4405
     *                                  unsupported characters.</p>
4406
     *
4407
     * @psalm-mutation-free
4408
     *
4409
     * @return static
4410
     *                <p>Object whose $str contains only ASCII characters.</p>
4411
     *
4412
     * @phpstan-param ASCII::*_LANGUAGE_CODE $language
4413
     */
4414
    public function toAscii(string $language = 'en', bool $removeUnsupported = true): self
4415
    {
4416
        return static::create(
24✔
4417
            $this->ascii::to_ascii(
24✔
4418
                $this->str,
24✔
4419
                $language,
24✔
4420
                $removeUnsupported
24✔
4421
            ),
24✔
4422
            $this->encoding
24✔
4423
        );
24✔
4424
    }
4425

4426
    /**
4427
     * Returns a boolean representation of the given logical string value.
4428
     * For example, <strong>'true', '1', 'on' and 'yes'</strong> will return true. <strong>'false', '0',
4429
     * 'off', and 'no'</strong> will return false. In all instances, case is ignored.
4430
     * For other numeric strings, their sign will determine the return value.
4431
     * In addition, blank strings consisting of only whitespace will return
4432
     * false. For all other strings, the return value is a result of a
4433
     * boolean cast.
4434
     *
4435
     * EXAMPLE: <code>
4436
     * s('OFF')->toBoolean(); // false
4437
     * </code>
4438
     *
4439
     * @psalm-mutation-free
4440
     *
4441
     * @return bool
4442
     *              <p>A boolean value for the string.</p>
4443
     */
4444
    public function toBoolean(): bool
4445
    {
4446
        /**
4447
         * @psalm-suppress ArgumentTypeCoercion -> maybe the string looks like an int ;)
4448
         * @phpstan-ignore-next-line
4449
         */
4450
        return $this->utf8::to_boolean($this->str);
45✔
4451
    }
4452

4453
    /**
4454
     * Converts all characters in the string to lowercase.
4455
     *
4456
     * EXAMPLE: <code>
4457
     * s('FÒÔBÀŘ')->toLowerCase(); // 'fòôbàř'
4458
     * </code>
4459
     *
4460
     * @param bool        $tryToKeepStringLength [optional] <p>true === try to keep the string length: e.g. ẞ -> ß</p>
4461
     * @param string|null $lang                  [optional] <p>Set the language for special cases: az, el, lt, tr</p>
4462
     *
4463
     * @psalm-mutation-free
4464
     *
4465
     * @return static
4466
     *                <p>Object with all characters of $str being lowercase.</p>
4467
     */
4468
    public function toLowerCase($tryToKeepStringLength = false, $lang = null): self
4469
    {
4470
        return static::create(
24✔
4471
            $this->utf8::strtolower(
24✔
4472
                $this->str,
24✔
4473
                $this->encoding,
24✔
4474
                false,
24✔
4475
                $lang,
24✔
4476
                $tryToKeepStringLength
24✔
4477
            ),
24✔
4478
            $this->encoding
24✔
4479
        );
24✔
4480
    }
4481

4482
    /**
4483
     * Converts each tab in the string to some number of spaces, as defined by
4484
     * $tabLength. By default, each tab is converted to 4 consecutive spaces.
4485
     *
4486
     * EXAMPLE: <code>
4487
     * s(' String speech = "Hi"')->toSpaces(); // '    String speech = "Hi"'
4488
     * </code>
4489
     *
4490
     * @param int $tabLength [optional] <p>Number of spaces to replace each tab with. Default: 4</p>
4491
     *
4492
     * @psalm-mutation-free
4493
     *
4494
     * @return static
4495
     *                <p>Object whose $str has had tabs switched to spaces.</p>
4496
     */
4497
    public function toSpaces(int $tabLength = 4): self
4498
    {
4499
        if ($tabLength === 4) {
19✔
4500
            $tab = '    ';
10✔
4501
        } elseif ($tabLength === 2) {
10✔
4502
            $tab = '  ';
4✔
4503
        } else {
4504
            $tab = \str_repeat(' ', $tabLength);
7✔
4505
        }
4506

4507
        return static::create(
19✔
4508
            \str_replace("\t", $tab, $this->str),
19✔
4509
            $this->encoding
19✔
4510
        );
19✔
4511
    }
4512

4513
    /**
4514
     * Return Stringy object as string, but you can also use (string) for automatically casting the object into a
4515
     * string.
4516
     *
4517
     * EXAMPLE: <code>
4518
     * s('fòôbàř')->toString(); // 'fòôbàř'
4519
     * </code>
4520
     *
4521
     * @psalm-mutation-free
4522
     *
4523
     * @return string
4524
     */
4525
    public function toString(): string
4526
    {
4527
        return (string) $this->str;
2,216✔
4528
    }
4529

4530
    /**
4531
     * Converts each occurrence of some consecutive number of spaces, as
4532
     * defined by $tabLength, to a tab. By default, each 4 consecutive spaces
4533
     * are converted to a tab.
4534
     *
4535
     * EXAMPLE: <code>
4536
     * s('    fòô    bàř')->toTabs(); // '   fòô bàř'
4537
     * </code>
4538
     *
4539
     * @param int $tabLength [optional] <p>Number of spaces to replace with a tab. Default: 4</p>
4540
     *
4541
     * @psalm-mutation-free
4542
     *
4543
     * @return static
4544
     *                <p>Object whose $str has had spaces switched to tabs.</p>
4545
     */
4546
    public function toTabs(int $tabLength = 4): self
4547
    {
4548
        if ($tabLength === 4) {
16✔
4549
            $tab = '    ';
10✔
4550
        } elseif ($tabLength === 2) {
7✔
4551
            $tab = '  ';
4✔
4552
        } else {
4553
            $tab = \str_repeat(' ', $tabLength);
4✔
4554
        }
4555

4556
        return static::create(
16✔
4557
            \str_replace($tab, "\t", $this->str),
16✔
4558
            $this->encoding
16✔
4559
        );
16✔
4560
    }
4561

4562
    /**
4563
     * Converts the first character of each word in the string to uppercase
4564
     * and all other chars to lowercase.
4565
     *
4566
     * EXAMPLE: <code>
4567
     * s('fòô bàř')->toTitleCase(); // 'Fòô Bàř'
4568
     * </code>
4569
     *
4570
     * @psalm-mutation-free
4571
     *
4572
     * @return static
4573
     *                <p>Object with all characters of $str being title-cased.</p>
4574
     */
4575
    public function toTitleCase(): self
4576
    {
4577
        return static::create(
15✔
4578
            $this->utf8::titlecase($this->str, $this->encoding),
15✔
4579
            $this->encoding
15✔
4580
        );
15✔
4581
    }
4582

4583
    /**
4584
     * Returns an ASCII version of the string. A set of non-ASCII characters are
4585
     * replaced with their closest ASCII counterparts, and the rest are removed
4586
     * unless instructed otherwise.
4587
     *
4588
     * EXAMPLE: <code>
4589
     * </code>
4590
     *
4591
     * @param bool   $strict  [optional] <p>Use "transliterator_transliterate()" from PHP-Intl | WARNING: bad
4592
     *                        performance | Default: false</p>
4593
     * @param string $unknown [optional] <p>Character use if character unknown. (default is ?)</p>
4594
     *
4595
     * @psalm-mutation-free
4596
     *
4597
     * @return static
4598
     *                <p>Object whose $str contains only ASCII characters.</p>
4599
     */
4600
    public function toTransliterate(bool $strict = false, string $unknown = '?'): self
4601
    {
4602
        return static::create(
34✔
4603
            $this->ascii::to_transliterate($this->str, $unknown, $strict),
34✔
4604
            $this->encoding
34✔
4605
        );
34✔
4606
    }
4607

4608
    /**
4609
     * Converts all characters in the string to uppercase.
4610
     *
4611
     * EXAMPLE: <code>
4612
     * s('fòôbàř')->toUpperCase(); // 'FÒÔBÀŘ'
4613
     * </code>
4614
     *
4615
     * @param bool        $tryToKeepStringLength [optional] <p>true === try to keep the string length: e.g. ẞ -> ß</p>
4616
     * @param string|null $lang                  [optional] <p>Set the language for special cases: az, el, lt, tr</p>
4617
     *
4618
     * @psalm-mutation-free
4619
     *
4620
     * @return static
4621
     *                <p>Object with all characters of $str being uppercase.</p>
4622
     */
4623
    public function toUpperCase($tryToKeepStringLength = false, $lang = null): self
4624
    {
4625
        return static::create(
28✔
4626
            $this->utf8::strtoupper($this->str, $this->encoding, false, $lang, $tryToKeepStringLength),
28✔
4627
            $this->encoding
28✔
4628
        );
28✔
4629
    }
4630

4631
    /**
4632
     * Returns a string with whitespace removed from the start and end of the
4633
     * string. Supports the removal of unicode whitespace. Accepts an optional
4634
     * string of characters to strip instead of the defaults.
4635
     *
4636
     * EXAMPLE: <code>
4637
     * s('  fòôbàř  ')->trim(); // 'fòôbàř'
4638
     * </code>
4639
     *
4640
     * @param string $chars [optional] <p>String of characters to strip. Default: null</p>
4641
     *
4642
     * @psalm-mutation-free
4643
     *
4644
     * @return static
4645
     *                <p>Object with a trimmed $str.</p>
4646
     */
4647
    public function trim(?string $chars = null): self
4648
    {
4649
        return static::create(
36✔
4650
            $this->utf8::trim($this->str, $chars),
36✔
4651
            $this->encoding
36✔
4652
        );
36✔
4653
    }
4654

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

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

4703
    /**
4704
     * Truncates the string to a given length. If $substring is provided, and
4705
     * truncating occurs, the string is further truncated so that the substring
4706
     * may be appended without exceeding the desired length.
4707
     *
4708
     * EXAMPLE: <code>
4709
     * s('What are your plans today?')->truncate(19, '...'); // 'What are your pl...'
4710
     * </code>
4711
     *
4712
     * @param int    $length    <p>Desired length of the truncated string.</p>
4713
     * @param string $substring [optional] <p>The substring to append if it can fit. Default: ''</p>
4714
     *
4715
     * @psalm-mutation-free
4716
     *
4717
     * @return static
4718
     *                <p>Object with the resulting $str after truncating.</p>
4719
     */
4720
    public function truncate(int $length, string $substring = ''): self
4721
    {
4722
        return static::create(
66✔
4723
            $this->utf8::str_truncate($this->str, $length, $substring, $this->encoding),
66✔
4724
            $this->encoding
66✔
4725
        );
66✔
4726
    }
4727

4728
    /**
4729
     * Returns a lowercase and trimmed string separated by underscores.
4730
     * Underscores are inserted before uppercase characters (with the exception
4731
     * of the first character of the string), and in place of spaces as well as
4732
     * dashes.
4733
     *
4734
     * EXAMPLE: <code>
4735
     * s('TestUCase')->underscored(); // 'test_u_case'
4736
     * </code>
4737
     *
4738
     * @psalm-mutation-free
4739
     *
4740
     * @return static
4741
     *                <p>Object with an underscored $str.</p>
4742
     */
4743
    public function underscored(): self
4744
    {
4745
        return $this->delimit('_');
48✔
4746
    }
4747

4748
    /**
4749
     * Returns an UpperCamelCase version of the supplied string. It trims
4750
     * surrounding spaces, capitalizes letters following digits, spaces, dashes
4751
     * and underscores, and removes spaces, dashes, underscores.
4752
     *
4753
     * EXAMPLE: <code>
4754
     * s('Upper Camel-Case')->upperCamelize(); // 'UpperCamelCase'
4755
     * </code>
4756
     *
4757
     * @psalm-mutation-free
4758
     *
4759
     * @return static
4760
     *                <p>Object with $str in UpperCamelCase.</p>
4761
     */
4762
    public function upperCamelize(): self
4763
    {
4764
        return static::create(
49✔
4765
            $this->utf8::str_upper_camelize($this->str, $this->encoding),
49✔
4766
            $this->encoding
49✔
4767
        );
49✔
4768
    }
4769

4770
    /**
4771
     * Converts the first character of the supplied string to upper case.
4772
     *
4773
     * EXAMPLE: <code>
4774
     * s('σ foo')->upperCaseFirst(); // 'Σ foo'
4775
     * </code>
4776
     *
4777
     * @psalm-mutation-free
4778
     *
4779
     * @return static
4780
     *                <p>Object with the first character of $str being upper case.</p>
4781
     */
4782
    public function upperCaseFirst(): self
4783
    {
4784
        return static::create($this->utf8::ucfirst($this->str, $this->encoding), $this->encoding);
18✔
4785
    }
4786

4787
    /**
4788
     * Simple url-decoding.
4789
     *
4790
     * e.g:
4791
     * 'test+test' => 'test test'
4792
     *
4793
     * EXAMPLE: <code>
4794
     * </code>
4795
     *
4796
     * @psalm-mutation-free
4797
     *
4798
     * @return static
4799
     */
4800
    public function urlDecode(): self
4801
    {
4802
        return static::create(\urldecode($this->str));
1✔
4803
    }
4804

4805
    /**
4806
     * Multi url-decoding + decode HTML entity + fix urlencoded-win1252-chars.
4807
     *
4808
     * e.g:
4809
     * 'test+test'                     => 'test test'
4810
     * 'D&#252;sseldorf'               => 'Düsseldorf'
4811
     * 'D%FCsseldorf'                  => 'Düsseldorf'
4812
     * 'D&#xFC;sseldorf'               => 'Düsseldorf'
4813
     * 'D%26%23xFC%3Bsseldorf'         => 'Düsseldorf'
4814
     * 'Düsseldorf'                   => 'Düsseldorf'
4815
     * 'D%C3%BCsseldorf'               => 'Düsseldorf'
4816
     * 'D%C3%83%C2%BCsseldorf'         => 'Düsseldorf'
4817
     * 'D%25C3%2583%25C2%25BCsseldorf' => 'Düsseldorf'
4818
     *
4819
     * EXAMPLE: <code>
4820
     * </code>
4821
     *
4822
     * @psalm-mutation-free
4823
     *
4824
     * @return static
4825
     */
4826
    public function urlDecodeMulti(): self
4827
    {
4828
        return static::create($this->utf8::urldecode($this->str));
1✔
4829
    }
4830

4831
    /**
4832
     * Simple url-decoding.
4833
     *
4834
     * e.g:
4835
     * 'test+test' => 'test+test
4836
     *
4837
     * EXAMPLE: <code>
4838
     * </code>
4839
     *
4840
     * @psalm-mutation-free
4841
     *
4842
     * @return static
4843
     */
4844
    public function urlDecodeRaw(): self
4845
    {
4846
        return static::create(\rawurldecode($this->str));
1✔
4847
    }
4848

4849
    /**
4850
     * Multi url-decoding + decode HTML entity + fix urlencoded-win1252-chars.
4851
     *
4852
     * e.g:
4853
     * 'test+test'                     => 'test+test'
4854
     * 'D&#252;sseldorf'               => 'Düsseldorf'
4855
     * 'D%FCsseldorf'                  => 'Düsseldorf'
4856
     * 'D&#xFC;sseldorf'               => 'Düsseldorf'
4857
     * 'D%26%23xFC%3Bsseldorf'         => 'Düsseldorf'
4858
     * 'Düsseldorf'                   => 'Düsseldorf'
4859
     * 'D%C3%BCsseldorf'               => 'Düsseldorf'
4860
     * 'D%C3%83%C2%BCsseldorf'         => 'Düsseldorf'
4861
     * 'D%25C3%2583%25C2%25BCsseldorf' => 'Düsseldorf'
4862
     *
4863
     * EXAMPLE: <code>
4864
     * </code>
4865
     *
4866
     * @psalm-mutation-free
4867
     *
4868
     * @return static
4869
     */
4870
    public function urlDecodeRawMulti(): self
4871
    {
4872
        return static::create($this->utf8::rawurldecode($this->str));
1✔
4873
    }
4874

4875
    /**
4876
     * Simple url-encoding.
4877
     *
4878
     * e.g:
4879
     * 'test test' => 'test+test'
4880
     *
4881
     * EXAMPLE: <code>
4882
     * </code>
4883
     *
4884
     * @psalm-mutation-free
4885
     *
4886
     * @return static
4887
     */
4888
    public function urlEncode(): self
4889
    {
4890
        return static::create(\urlencode($this->str));
1✔
4891
    }
4892

4893
    /**
4894
     * Simple url-encoding.
4895
     *
4896
     * e.g:
4897
     * 'test test' => 'test%20test'
4898
     *
4899
     * EXAMPLE: <code>
4900
     * </code>
4901
     *
4902
     * @psalm-mutation-free
4903
     *
4904
     * @return static
4905
     */
4906
    public function urlEncodeRaw(): self
4907
    {
4908
        return static::create(\rawurlencode($this->str));
1✔
4909
    }
4910

4911
    /**
4912
     * Converts the string into an URL slug. This includes replacing non-ASCII
4913
     * characters with their closest ASCII equivalents, removing remaining
4914
     * non-ASCII and non-alphanumeric characters, and replacing whitespace with
4915
     * $separator. The separator defaults to a single dash, and the string
4916
     * is also converted to lowercase.
4917
     *
4918
     * EXAMPLE: <code>
4919
     * s('Using strings like fòô bàř - 1$')->urlify(); // 'using-strings-like-foo-bar-1-dollar'
4920
     * </code>
4921
     *
4922
     * @param string                $separator    [optional] <p>The string used to replace whitespace. Default: '-'</p>
4923
     * @param string                $language     [optional] <p>The language for the url. Default: 'en'</p>
4924
     * @param array<string, string> $replacements [optional] <p>A map of replaceable strings.</p>
4925
     * @param bool                  $strToLower   [optional] <p>string to lower. Default: true</p>
4926
     *
4927
     * @psalm-mutation-free
4928
     *
4929
     * @return static
4930
     *                <p>Object whose $str has been converted to an URL slug.</p>
4931
     *
4932
     * @phpstan-param ASCII::*_LANGUAGE_CODE $language
4933
     *
4934
     * @psalm-suppress ImpureMethodCall :/
4935
     */
4936
    public function urlify(
4937
        string $separator = '-',
4938
        string $language = 'en',
4939
        array $replacements = [],
4940
        bool $strToLower = true
4941
    ): self {
4942
        // init
4943
        $str = $this->str;
32✔
4944

4945
        foreach ($replacements as $from => $to) {
32✔
4946
            $str = \str_replace($from, $to, $str);
32✔
4947
        }
4948

4949
        return static::create(
32✔
4950
            URLify::slug(
32✔
4951
                $str,
32✔
4952
                $language,
32✔
4953
                $separator,
32✔
4954
                $strToLower
32✔
4955
            ),
32✔
4956
            $this->encoding
32✔
4957
        );
32✔
4958
    }
4959

4960
    /**
4961
     * Converts the string into an valid UTF-8 string.
4962
     *
4963
     * EXAMPLE: <code>
4964
     * s('Düsseldorf')->utf8ify(); // 'Düsseldorf'
4965
     * </code>
4966
     *
4967
     * @psalm-mutation-free
4968
     *
4969
     * @return static
4970
     */
4971
    public function utf8ify(): self
4972
    {
4973
        return static::create($this->utf8::cleanup($this->str), $this->encoding);
2✔
4974
    }
4975

4976
    /**
4977
     * Convert a string into an array of words.
4978
     *
4979
     * EXAMPLE: <code>
4980
     * </code>
4981
     *
4982
     * @param string   $char_list           [optional] <p>Additional chars for the definition of "words".</p>
4983
     * @param bool     $remove_empty_values [optional] <p>Remove empty values.</p>
4984
     * @param int|null $remove_short_values [optional] <p>The min. string length or null to disable</p>
4985
     *
4986
     * @psalm-mutation-free
4987
     *
4988
     * @return static[]
4989
     *
4990
     * @phpstan-return array<int,static>
4991
     */
4992
    public function words(
4993
        string $char_list = '',
4994
        bool $remove_empty_values = false,
4995
        ?int $remove_short_values = null
4996
    ): array {
4997
        if ($remove_short_values === null) {
16✔
4998
            $strings = $this->utf8::str_to_words(
16✔
4999
                $this->str,
16✔
5000
                $char_list,
16✔
5001
                $remove_empty_values
16✔
5002
            );
16✔
5003
        } else {
5004
            $strings = $this->utf8::str_to_words(
2✔
5005
                $this->str,
2✔
5006
                $char_list,
2✔
5007
                $remove_empty_values,
2✔
5008
                $remove_short_values
2✔
5009
            );
2✔
5010
        }
5011

5012
        /** @noinspection AlterInForeachInspection */
5013
        foreach ($strings as &$string) {
16✔
5014
            $string = static::create($string);
16✔
5015
        }
5016

5017
        /** @noinspection PhpSillyAssignmentInspection */
5018
        /** @var static[] $strings */
5019
        $strings = $strings;
16✔
5020

5021
        return $strings;
16✔
5022
    }
5023

5024
    /**
5025
     * Convert a string into an collection of words.
5026
     *
5027
     * EXAMPLE: <code>
5028
     * S::create('中文空白 oöäü#s')->wordsCollection('#', true)->toStrings(); // ['中文空白', 'oöäü#s']
5029
     * </code>
5030
     *
5031
     * @param string   $char_list           [optional] <p>Additional chars for the definition of "words".</p>
5032
     * @param bool     $remove_empty_values [optional] <p>Remove empty values.</p>
5033
     * @param int|null $remove_short_values [optional] <p>The min. string length or null to disable</p>
5034
     *
5035
     * @psalm-mutation-free
5036
     *
5037
     * @return CollectionStringy|static[]
5038
     *                                    <p>An collection of Stringy objects.</p>
5039
     *
5040
     * @phpstan-return CollectionStringy<int,static>
5041
     */
5042
    public function wordsCollection(
5043
        string $char_list = '',
5044
        bool $remove_empty_values = false,
5045
        ?int $remove_short_values = null
5046
    ): CollectionStringy {
5047
        /**
5048
         * @psalm-suppress ImpureMethodCall -> add more psalm stuff to the collection class
5049
         */
5050
        return CollectionStringy::create(
2✔
5051
            $this->words(
2✔
5052
                $char_list,
2✔
5053
                $remove_empty_values,
2✔
5054
                $remove_short_values
2✔
5055
            )
2✔
5056
        );
2✔
5057
    }
5058

5059
    /**
5060
     * Surrounds $str with the given substring.
5061
     *
5062
     * EXAMPLE: <code>
5063
     * </code>
5064
     *
5065
     * @param string $substring <p>The substring to add to both sides.</P>
5066
     *
5067
     * @psalm-mutation-free
5068
     *
5069
     * @return static
5070
     *                <p>Object whose $str had the substring both prepended and appended.</p>
5071
     */
5072
    public function wrap(string $substring): self
5073
    {
5074
        return $this->surround($substring);
10✔
5075
    }
5076

5077
    /**
5078
     * Returns the replacements for the toAscii() method.
5079
     *
5080
     * @psalm-mutation-free
5081
     *
5082
     * @return array<string, array<int, string>>
5083
     *                                           <p>An array of replacements.</p>
5084
     *
5085
     * @deprecated   this is only here for backward-compatibly reasons
5086
     */
5087
    protected function charsArray(): array
5088
    {
5089
        return $this->ascii::charsArrayWithMultiLanguageValues();
1✔
5090
    }
5091

5092
    /**
5093
     * Returns true if $str matches the supplied pattern, false otherwise.
5094
     *
5095
     * @param string $pattern <p>Regex pattern to match against.</p>
5096
     *
5097
     * @psalm-mutation-free
5098
     *
5099
     * @return bool
5100
     *              <p>Whether or not $str matches the pattern.</p>
5101
     */
5102
    protected function matchesPattern(string $pattern): bool
5103
    {
5104
        return $this->utf8::str_matches_pattern($this->str, $pattern);
27✔
5105
    }
5106
}
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

© 2026 Coveralls, Inc