GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Branch develop (e41bc0)
by Baptiste
05:34
created

Str::startsWith()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 10
ccs 6
cts 6
cp 1
crap 3
rs 10
1
<?php
2
declare(strict_types = 1);
3
4
namespace Innmind\Immutable;
5
6
use Innmind\Immutable\{
7
    Exception\RegexException,
8
    Exception\SubstringException,
9
    Exception\LogicException
10
};
11
12
class Str implements PrimitiveInterface, StringableInterface
13
{
14
    const PAD_RIGHT = STR_PAD_RIGHT;
15
    const PAD_LEFT = STR_PAD_LEFT;
16
    const PAD_BOTH = STR_PAD_BOTH;
17
    const PREG_NO_FLAGS = 0;
18
    const PREG_SPLIT_NO_EMPTY = PREG_SPLIT_NO_EMPTY;
19
    const PREG_SPLIT_DELIM_CAPTURE = PREG_SPLIT_DELIM_CAPTURE;
20
    const PREG_SPLIT_OFFSET_CAPTURE = PREG_SPLIT_OFFSET_CAPTURE;
21
    const PREG_OFFSET_CAPTURE = PREG_OFFSET_CAPTURE;
22
23
    private $value;
24
    private $encoding;
25
26 548
    public function __construct(string $value, string $encoding = null)
27
    {
28 548
        $this->value = $value;
29 548
        $this->encoding = $encoding;
30 548
    }
31
32 10
    public static function of(string $value, string $encoding = null): self
33
    {
34 10
        return new self($value, $encoding);
35
    }
36
37
    /**
38
     * {@inheritdoc}
39
     */
40 2
    public function toPrimitive()
41
    {
42 2
        return $this->value;
43
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48 346
    public function __toString(): string
49
    {
50 346
        return $this->value;
51
    }
52
53 68
    public function encoding(): self
54
    {
55 68
        if (\is_null($this->encoding)) {
56 58
            $this->encoding = \mb_internal_encoding();
57
        }
58
59 68
        return new self($this->encoding);
0 ignored issues
show
Bug introduced by
It seems like $this->encoding can also be of type boolean; however, parameter $value of Innmind\Immutable\Str::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

59
        return new self(/** @scrutinizer ignore-type */ $this->encoding);
Loading history...
60
    }
61
62 30
    public function toEncoding(string $encoding): self
63
    {
64 30
        return new self($this->value, $encoding);
65
    }
66
67
    /**
68
     * Split the string into a collection of ones
69
     *
70
     * @param string $delimiter
71
     *
72
     * @return StreamInterface<self>
73
     */
74 14
    public function split(string $delimiter = null): StreamInterface
75
    {
76 14
        if (\is_null($delimiter) || $delimiter === '') {
77 6
            return $this->chunk();
78
        }
79
80 10
        $parts = \explode($delimiter, $this->value);
81 10
        $stream = new Stream(self::class);
82
83 10
        foreach ($parts as $part) {
84 10
            $stream = $stream->add(new self($part, $this->encoding));
85
        }
86
87 10
        return $stream;
88
    }
89
90
    /**
91
     * Returns a collection of the string splitted by the given chunk size
92
     *
93
     * @param int $size
94
     *
95
     * @return StreamInterface<self>
96
     */
97 14
    public function chunk(int $size = 1): StreamInterface
98
    {
99 14
        $stream = new Stream(self::class);
100 14
        $string = $this;
101
102 14
        while ($string->length() > 0) {
103 14
            $stream = $stream->add($string->substring(0, $size));
104 14
            $string = $string->substring($size);
105
        }
106
107 14
        return $stream;
108
    }
109
110
    /**
111
     * Returns the position of the first occurence of the string
112
     *
113
     * @param string $needle
114
     * @param int $offset
115
     *
116
     * @throws SubstringException If the string is not found
117
     *
118
     * @return int
119
     */
120 14
    public function position(string $needle, int $offset = 0): int
121
    {
122 14
        $position = \mb_strpos($this->value, $needle, $offset, (string) $this->encoding());
123
124 14
        if ($position === false) {
125 8
            throw new SubstringException(\sprintf(
126 8
                'Substring "%s" not found',
127 8
                $needle
128
            ));
129
        }
130
131 12
        return (int) $position;
132
    }
133
134
    /**
135
     * Replace all occurences of the search string with the replacement one
136
     *
137
     * @param string $search
138
     * @param string $replacement
139
     *
140
     * @return self
141
     */
142 4
    public function replace(string $search, string $replacement): self
143
    {
144 4
        if (!$this->contains($search)) {
145 2
            return $this;
146
        }
147
148
        return $this
149 4
            ->split($search)
150 4
            ->join($replacement);
151
    }
152
153
    /**
154
     * Returns the string following the given delimiter
155
     *
156
     * @param string $delimiter
157
     *
158
     * @throws SubstringException If the string is not found
159
     *
160
     * @return self
161
     */
162 6
    public function str(string $delimiter): self
163
    {
164 6
        $sub = \mb_strstr($this->value, $delimiter, false, (string) $this->encoding());
165
166 6
        if ($sub === false) {
167 2
            throw new SubstringException(\sprintf(
168 2
                'Substring "%s" not found',
169 2
                $delimiter
170
            ));
171
        }
172
173 4
        return new self($sub, $this->encoding);
0 ignored issues
show
Bug introduced by
It seems like $this->encoding can also be of type boolean; however, parameter $encoding of Innmind\Immutable\Str::__construct() does only seem to accept null|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

173
        return new self($sub, /** @scrutinizer ignore-type */ $this->encoding);
Loading history...
174
    }
175
176
    /**
177
     * Return the string in upper case
178
     *
179
     * @return self
180
     */
181 6
    public function toUpper(): self
182
    {
183 6
        return new self(\mb_strtoupper($this->value), $this->encoding);
184
    }
185
186
    /**
187
     * Return the string in lower case
188
     *
189
     * @return self
190
     */
191 4
    public function toLower(): self
192
    {
193 4
        return new self(\mb_strtolower($this->value), $this->encoding);
194
    }
195
196
    /**
197
     * Return the string length
198
     *
199
     * @return int
200
     */
201 40
    public function length(): int
202
    {
203 40
        return \mb_strlen($this->value, (string) $this->encoding());
204
    }
205
206 2
    public function empty(): bool
207
    {
208 2
        return $this->value === '';
209
    }
210
211
    /**
212
     * Reverse the string
213
     *
214
     * @return self
215
     */
216 2
    public function reverse(): self
217
    {
218
        return $this
219 2
            ->chunk()
220 2
            ->reverse()
221 2
            ->join('');
222
    }
223
224
    /**
225
     * Pad to the right
226
     *
227
     * @param int $length
228
     * @param string $character
229
     *
230
     * @return self
231
     */
232 2
    public function rightPad(int $length, string $character = ' '): self
233
    {
234 2
        return $this->pad($length, $character, self::PAD_RIGHT);
235
    }
236
237
    /**
238
     * Pad to the left
239
     *
240
     * @param int $length
241
     * @param string $character
242
     *
243
     * @return self
244
     */
245 2
    public function leftPad(int $length, string $character = ' '): self
246
    {
247 2
        return $this->pad($length, $character, self::PAD_LEFT);
248
    }
249
250
    /**
251
     * Pad both sides
252
     *
253
     * @param int $length
254
     * @param string $character
255
     *
256
     * @return self
257
     */
258 2
    public function uniPad(int $length, string $character = ' '): self
259
    {
260 2
        return $this->pad($length, $character, self::PAD_BOTH);
261
    }
262
263
    /**
264
     * Find length of initial segment not matching mask
265
     *
266
     * @param string $mask
267
     * @param int $start
268
     * @param int $length
269
     *
270
     * @return int
271
     */
272 2
    public function cspn(string $mask, int $start = 0, int $length = null): int
273
    {
274 2
        if ($length === null) {
275 2
            $value = \strcspn($this->value, $mask, $start);
276
        } else {
277 2
            $value = \strcspn(
278 2
                $this->value,
279 2
                $mask,
280 2
                $start,
281 2
                $length
282
            );
283
        }
284
285 2
        return (int) $value;
286
    }
287
288
    /**
289
     * Repeat the string n times
290
     *
291
     * @param int $repeat
292
     *
293
     * @return self
294
     */
295 2
    public function repeat(int $repeat): self
296
    {
297 2
        return new self(\str_repeat($this->value, $repeat), $this->encoding);
298
    }
299
300
    /**
301
     * Shuffle the string
302
     *
303
     * @return self
304
     */
305 4
    public function shuffle(): self
306
    {
307 4
        $parts = $this->chunk()->toPrimitive();
308 4
        \shuffle($parts);
309
310 4
        return new self(\implode('', $parts), $this->encoding);
311
    }
312
313
    /**
314
     * Strip slashes
315
     *
316
     * @return self
317
     */
318 2
    public function stripSlashes(): self
319
    {
320 2
        return new self(\stripslashes($this->value), $this->encoding);
321
    }
322
323
    /**
324
     * Strip C-like slashes
325
     *
326
     * @return self
327
     */
328 2
    public function stripCSlashes(): self
329
    {
330 2
        return new self(\stripcslashes($this->value), $this->encoding);
331
    }
332
333
    /**
334
     * Return the word count
335
     *
336
     * @param string $charlist
337
     *
338
     * @return int
339
     */
340 2
    public function wordCount(string $charlist = ''): int
341
    {
342 2
        return (int) \str_word_count(
343 2
            $this->value,
344 2
            0,
345 2
            $charlist
346
        );
347
    }
348
349
    /**
350
     * Return the collection of words
351
     *
352
     * @param string $charlist
353
     *
354
     * @return MapInterface<int, self>
355
     */
356 2
    public function words(string $charlist = ''): MapInterface
357
    {
358 2
        $words = \str_word_count($this->value, 2, $charlist);
359 2
        $map = new Map('int', self::class);
360
361 2
        foreach ($words as $position => $word) {
362 2
            $map = $map->put($position, new self($word, $this->encoding));
363
        }
364
365 2
        return $map;
366
    }
367
368
    /**
369
     * Split the string using a regular expression
370
     *
371
     * @param string $regex
372
     * @param int $limit
373
     *
374
     * @return StreamInterface<self>
375
     */
376 4
    public function pregSplit(string $regex, int $limit = -1): StreamInterface
377
    {
378 4
        $strings = \preg_split($regex, $this->value, $limit);
379 4
        $stream = new Stream(self::class);
380
381 4
        foreach ($strings as $string) {
382 4
            $stream = $stream->add(new self($string, $this->encoding));
383
        }
384
385 4
        return $stream;
386
    }
387
388
    /**
389
     * Check if the string match the given regular expression
390
     *
391
     * @param string $regex
392
     *
393
     * @throws Exception If the regex failed
394
     *
395
     * @return bool
396
     */
397 4
    public function matches(string $regex): bool
398
    {
399 4
        if (\func_num_args() !== 1) {
400
            throw new LogicException('Offset is no longer supported');
401
        }
402
403 4
        return RegExp::of($regex)->matches($this);
404
    }
405
406
    /**
407
     * Return a collection of the elements matching the regex
408
     *
409
     * @deprecated replaced by self::capture, to be removed in 3.0
410
     *
411
     * @param string $regex
412
     * @param int $offset
413
     * @param int $flags
414
     *
415
     * @throws Exception If the regex failed
416
     *
417
     * @return MapInterface<scalar, self>
418
     */
419
    public function getMatches(
420
        string $regex,
421
        int $offset = 0,
422
        int $flags = self::PREG_NO_FLAGS
423
    ): MapInterface {
424
        return $this->capture($regex, $offset, $flags);
425
    }
426
427
    /**
428
     * Return a collection of the elements matching the regex
429
     *
430
     * @param string $regex
431
     *
432
     * @throws Exception If the regex failed
433
     *
434
     * @return MapInterface<scalar, self>
435
     */
436 6
    public function capture(string $regex): MapInterface
437
    {
438 6
        if (\func_num_args() !== 1) {
439
            throw new LogicException('Offset and flags are no longer supported');
440
        }
441
442 6
        return RegExp::of($regex)->capture($this);
443
    }
444
445
    /**
446
     * Replace part of the string by using a regular expression
447
     *
448
     * @param string $regex
449
     * @param string $replacement
450
     * @param int $limit
451
     *
452
     * @throws Exception If the regex failed
453
     *
454
     * @return self
455
     */
456 2
    public function pregReplace(
457
        string $regex,
458
        string $replacement,
459
        int $limit = -1
460
    ): self {
461 2
        $value = \preg_replace(
462 2
            $regex,
463 2
            $replacement,
464 2
            $this->value,
465 2
            $limit
466
        );
467
468 2
        if ($value === null) {
469
            throw new RegexException('', \preg_last_error());
470
        }
471
472 2
        return new self($value, $this->encoding);
473
    }
474
475
    /**
476
     * Return part of the string
477
     *
478
     * @param int $start
479
     * @param int $length
480
     *
481
     * @return self
482
     */
483 36
    public function substring(int $start, int $length = null): self
484
    {
485 36
        if ($this->length() === 0) {
486 2
            return $this;
487
        }
488
489 36
        $sub = \mb_substr($this->value, $start, $length, (string) $this->encoding());
490
491 36
        return new self($sub, $this->encoding);
0 ignored issues
show
Bug introduced by
It seems like $this->encoding can also be of type boolean; however, parameter $encoding of Innmind\Immutable\Str::__construct() does only seem to accept null|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

491
        return new self($sub, /** @scrutinizer ignore-type */ $this->encoding);
Loading history...
492
    }
493
494 2
    public function take(int $size): self
495
    {
496 2
        return $this->substring(0, $size);
497
    }
498
499 4
    public function takeEnd(int $size): self
500
    {
501 4
        return $this->substring(-$size);
502
    }
503
504 2
    public function drop(int $size): self
505
    {
506 2
        return $this->substring($size);
507
    }
508
509 2
    public function dropEnd(int $size): self
510
    {
511 2
        return $this->substring(0, $this->length() - $size);
512
    }
513
514
    /**
515
     * Return a formatted string
516
     *
517
     * @return self
518
     */
519 2
    public function sprintf(...$values): self
520
    {
521 2
        return new self(\sprintf($this->value, ...$values), $this->encoding);
522
    }
523
524
    /**
525
     * Return the string with the first letter as uppercase
526
     *
527
     * @return self
528
     */
529 4
    public function ucfirst(): self
530
    {
531
        return $this
532 4
            ->substring(0, 1)
533 4
            ->toUpper()
534 4
            ->append((string) $this->substring(1));
535
    }
536
537
    /**
538
     * Return the string with the first letter as lowercase
539
     *
540
     * @return self
541
     */
542 2
    public function lcfirst(): self
543
    {
544
        return $this
545 2
            ->substring(0, 1)
546 2
            ->toLower()
547 2
            ->append((string) $this->substring(1));
548
    }
549
550
    /**
551
     * Return a CamelCase representation of the string
552
     *
553
     * @return self
554
     */
555 2
    public function camelize(): self
556
    {
557
        return $this
558 2
            ->pregSplit('/_| /')
559 2
            ->map(function(self $part) {
560 2
                return $part->ucfirst();
561 2
            })
562 2
            ->join('')
563 2
            ->toEncoding((string) $this->encoding());
564
    }
565
566
    /**
567
     * Append a string at the end of the current one
568
     *
569
     * @param string $string
570
     *
571
     * @return self
572
     */
573 8
    public function append(string $string): self
574
    {
575 8
        return new self((string) $this.$string, $this->encoding);
576
    }
577
578
    /**
579
     * Prepend a string at the beginning of the current one
580
     *
581
     * @param string $string
582
     *
583
     * @return self
584
     */
585 2
    public function prepend(string $string): self
586
    {
587 2
        return new self($string.(string) $this, $this->encoding);
588
    }
589
590
    /**
591
     * Check if the 2 strings are equal
592
     *
593
     * @param self $string
594
     *
595
     * @return bool
596
     */
597 76
    public function equals(self $string): bool
598
    {
599 76
        return (string) $this === (string) $string;
600
    }
601
602
    /**
603
     * Trim the string
604
     *
605
     * @param string $mask
606
     *
607
     * @return self
608
     */
609 2
    public function trim(string $mask = null): self
610
    {
611 2
        return new self(
612 2
            $mask === null ? \trim((string) $this) : \trim((string) $this, $mask),
613 2
            $this->encoding
614
        );
615
    }
616
617
    /**
618
     * Trim the right side of the string
619
     *
620
     * @param string $mask
621
     *
622
     * @return self
623
     */
624 2
    public function rightTrim(string $mask = null): self
625
    {
626 2
        return new self(
627 2
            $mask === null ? \rtrim((string) $this) : \rtrim((string) $this, $mask),
628 2
            $this->encoding
629
        );
630
    }
631
632
    /**
633
     * Trim the left side of the string
634
     *
635
     * @param string $mask
636
     *
637
     * @return self
638
     */
639 2
    public function leftTrim(string $mask = null): self
640
    {
641 2
        return new self(
642 2
            $mask === null ? \ltrim((string) $this) : \ltrim((string) $this, $mask),
643 2
            $this->encoding
644
        );
645
    }
646
647
    /**
648
     * Check if the given string is present in the current one
649
     *
650
     * @param string $value
651
     *
652
     * @return bool
653
     */
654 6
    public function contains(string $value): bool
655
    {
656
        try {
657 6
            $this->position($value);
658
659 6
            return true;
660 4
        } catch (SubstringException $e) {
661 4
            return false;
662
        }
663
    }
664
665
    /**
666
     * Check if the current string starts with the given string
667
     *
668
     * @param string $value
669
     *
670
     * @return bool
671
     */
672 2
    public function startsWith(string $value): bool
673
    {
674 2
        if ($value === '') {
675 2
            return true;
676
        }
677
678
        try {
679 2
            return $this->position($value) === 0;
680 2
        } catch (SubstringException $e) {
681 2
            return false;
682
        }
683
    }
684
685
    /**
686
     * Check if the current string ends with the given string
687
     *
688
     * @param string $value
689
     *
690
     * @return bool
691
     */
692 2
    public function endsWith(string $value): bool
693
    {
694 2
        if ($value === '') {
695 2
            return true;
696
        }
697
698 2
        return (string) $this->takeEnd(self::of($value, $this->encoding)->length()) === $value;
699
    }
700
701
    /**
702
     * Quote regular expression characters
703
     *
704
     * @param string $delimiter
705
     *
706
     * @return self
707
     */
708 2
    public function pregQuote(string $delimiter = ''): self
709
    {
710 2
        return new self(\preg_quote((string) $this, $delimiter), $this->encoding);
711
    }
712
713
    /**
714
     * Pad the string
715
     *
716
     * @param int $length
717
     * @param string $character
718
     * @param int $direction
719
     *
720
     * @return self
721
     */
722 2
    private function pad(
723
        int $length,
724
        string $character = ' ',
725
        int $direction = self::PAD_RIGHT
726
    ): self {
727 2
        return new self(\str_pad(
728 2
            $this->value,
729 2
            $length,
730 2
            $character,
731 2
            $direction
732 2
        ), $this->encoding);
733
    }
734
}
735