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
Push — develop ( 3d4c79...7ab88f )
by Baptiste
05:30
created

Str::toString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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
final class Str
13
{
14
    private const PAD_RIGHT = STR_PAD_RIGHT;
15
    private const PAD_LEFT = STR_PAD_LEFT;
16
    private const PAD_BOTH = STR_PAD_BOTH;
17
    private const PREG_NO_FLAGS = 0;
18
    private const PREG_SPLIT_NO_EMPTY = PREG_SPLIT_NO_EMPTY;
19
    private const PREG_SPLIT_DELIM_CAPTURE = PREG_SPLIT_DELIM_CAPTURE;
20
    private const PREG_SPLIT_OFFSET_CAPTURE = PREG_SPLIT_OFFSET_CAPTURE;
21
    private const PREG_OFFSET_CAPTURE = PREG_OFFSET_CAPTURE;
22
23
    private string $value;
24
    private ?string $encoding;
25
26 337
    private function __construct(string $value, string $encoding = null)
27
    {
28 337
        $this->value = $value;
29 337
        $this->encoding = $encoding;
30 337
    }
31
32 337
    public static function of(string $value, string $encoding = null): self
33
    {
34 337
        return new self($value, $encoding);
35
    }
36
37
    /**
38
     * {@inheritdoc}
39
     */
40 335
    public function toString(): string
41
    {
42 335
        return $this->value;
43
    }
44
45 315
    public function encoding(): self
46
    {
47 315
        if (\is_null($this->encoding)) {
48
            /** @var string */
49 311
            $this->encoding = \mb_internal_encoding();
0 ignored issues
show
Bug introduced by
The property encoding does not exist on string.
Loading history...
50
        }
51
52 315
        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

52
        return new self(/** @scrutinizer ignore-type */ $this->encoding);
Loading history...
53
    }
54
55 16
    public function toEncoding(string $encoding): self
56
    {
57 16
        return new self($this->value, $encoding);
58
    }
59
60
    /**
61
     * Split the string into a collection of ones
62
     *
63
     * @return Sequence<self>
64
     */
65 16
    public function split(string $delimiter = null): Sequence
66
    {
67 16
        if (\is_null($delimiter) || $delimiter === '') {
68 3
            return $this->chunk();
69
        }
70
71 14
        $parts = \explode($delimiter, $this->value);
72
        /** @var Sequence<self> */
73 14
        $sequence = Sequence::of(self::class);
74
75 14
        foreach ($parts as $part) {
76 14
            $sequence = ($sequence)(new self($part, $this->encoding));
77
        }
78
79 14
        return $sequence;
80
    }
81
82
    /**
83
     * Returns a collection of the string splitted by the given chunk size
84
     *
85
     * @return Sequence<self>
86
     */
87 8
    public function chunk(int $size = 1): Sequence
88
    {
89
        /** @var Sequence<self> */
90 8
        $sequence = Sequence::of(self::class);
91
        /** @var list<string> */
92 8
        $parts = \mb_str_split($this->value, $size, $this->encoding()->toString());
93
94 8
        foreach ($parts as $value) {
95 8
            $sequence = ($sequence)(new self($value, $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

95
            $sequence = ($sequence)(new self($value, /** @scrutinizer ignore-type */ $this->encoding));
Loading history...
96
        }
97
98 8
        return $sequence;
99
    }
100
101
    /**
102
     * Returns the position of the first occurence of the string
103
     *
104
     * @throws SubstringException If the string is not found
105
     */
106 298
    public function position(string $needle, int $offset = 0): int
107
    {
108 298
        $position = \mb_strpos($this->value, $needle, $offset, $this->encoding()->toString());
109
110 298
        if ($position === false) {
111 296
            throw new SubstringException(\sprintf(
112 296
                'Substring "%s" not found',
113 296
                $needle
114
            ));
115
        }
116
117 15
        return (int) $position;
118
    }
119
120
    /**
121
     * Replace all occurences of the search string with the replacement one
122
     */
123 2
    public function replace(string $search, string $replacement): self
124
    {
125 2
        if (!$this->contains($search)) {
126 1
            return $this;
127
        }
128
129
        /**
130
         * @psalm-suppress InvalidArgument
131
         * @var Sequence<string>
132
         */
133
        $parts = $this
134 2
            ->split($search)
135 2
            ->toSequenceOf('string', fn($v) => yield $v->toString());
136
137 2
        return join($replacement, $parts);
0 ignored issues
show
Bug introduced by
$parts of type Innmind\Immutable\Sequence is incompatible with the type Innmind\Immutable\Set expected by parameter $structure of Innmind\Immutable\join(). ( Ignorable by Annotation )

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

137
        return join($replacement, /** @scrutinizer ignore-type */ $parts);
Loading history...
138
    }
139
140
    /**
141
     * Returns the string following the given delimiter
142
     *
143
     * @throws SubstringException If the string is not found
144
     */
145 3
    public function str(string $delimiter): self
146
    {
147 3
        $sub = \mb_strstr($this->value, $delimiter, false, $this->encoding()->toString());
148
149 3
        if ($sub === false) {
150 1
            throw new SubstringException(\sprintf(
151 1
                'Substring "%s" not found',
152 1
                $delimiter
153
            ));
154
        }
155
156 2
        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

156
        return new self($sub, /** @scrutinizer ignore-type */ $this->encoding);
Loading history...
157
    }
158
159
    /**
160
     * Return the string in upper case
161
     */
162 3
    public function toUpper(): self
163
    {
164 3
        return new self(\mb_strtoupper($this->value), $this->encoding);
165
    }
166
167
    /**
168
     * Return the string in lower case
169
     */
170 3
    public function toLower(): self
171
    {
172 3
        return new self(\mb_strtolower($this->value), $this->encoding);
173
    }
174
175
    /**
176
     * Return the string length
177
     */
178 5
    public function length(): int
179
    {
180 5
        return \mb_strlen($this->value, $this->encoding()->toString());
181
    }
182
183 12
    public function empty(): bool
184
    {
185 12
        return $this->value === '';
186
    }
187
188
    /**
189
     * Reverse the string
190
     */
191 2
    public function reverse(): self
192
    {
193
        /**
194
         * @psalm-suppress InvalidArgument
195
         * @var Sequence<string>
196
         */
197
        $parts = $this
198 2
            ->chunk()
199 2
            ->reverse()
200 2
            ->toSequenceOf('string', fn($v) => yield $v->toString());
201
202 2
        return join('', $parts)->toEncoding($this->encoding()->toString());
0 ignored issues
show
Bug introduced by
$parts of type Innmind\Immutable\Sequence is incompatible with the type Innmind\Immutable\Set expected by parameter $structure of Innmind\Immutable\join(). ( Ignorable by Annotation )

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

202
        return join('', /** @scrutinizer ignore-type */ $parts)->toEncoding($this->encoding()->toString());
Loading history...
203
    }
204
205
    /**
206
     * Pad to the right
207
     */
208 1
    public function rightPad(int $length, string $character = ' '): self
209
    {
210 1
        return $this->pad($length, $character, self::PAD_RIGHT);
211
    }
212
213
    /**
214
     * Pad to the left
215
     */
216 1
    public function leftPad(int $length, string $character = ' '): self
217
    {
218 1
        return $this->pad($length, $character, self::PAD_LEFT);
219
    }
220
221
    /**
222
     * Pad both sides
223
     */
224 1
    public function uniPad(int $length, string $character = ' '): self
225
    {
226 1
        return $this->pad($length, $character, self::PAD_BOTH);
227
    }
228
229
    /**
230
     * Find length of initial segment not matching mask
231
     */
232 1
    public function cspn(string $mask, int $start = 0, int $length = null): int
233
    {
234 1
        if ($length === null) {
235 1
            $value = \strcspn($this->value, $mask, $start);
236
        } else {
237 1
            $value = \strcspn(
238 1
                $this->value,
239 1
                $mask,
240 1
                $start,
241 1
                $length
242
            );
243
        }
244
245 1
        return (int) $value;
246
    }
247
248
    /**
249
     * Repeat the string n times
250
     */
251 1
    public function repeat(int $repeat): self
252
    {
253 1
        return new self(\str_repeat($this->value, $repeat), $this->encoding);
254
    }
255
256
    /**
257
     * Shuffle the string
258
     */
259 2
    public function shuffle(): self
260
    {
261
        /** @psalm-suppress InvalidArgument */
262 2
        $parts = unwrap(
263
            $this
0 ignored issues
show
Bug introduced by
$this->chunk()->toSequen...ion(...) { /* ... */ }) of type Innmind\Immutable\Sequence is incompatible with the type Innmind\Immutable\Set expected by parameter $structure of Innmind\Immutable\unwrap(). ( Ignorable by Annotation )

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

263
            /** @scrutinizer ignore-type */ $this
Loading history...
264 2
                ->chunk()
265 2
                ->toSequenceOf('string', fn($v) => yield $v->toString())
266
        );
267 2
        \shuffle($parts);
268
269 2
        return new self(\implode('', $parts), $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

269
        return new self(\implode('', $parts), /** @scrutinizer ignore-type */ $this->encoding);
Loading history...
270
    }
271
272 1
    public function stripSlashes(): self
273
    {
274 1
        return new self(\stripslashes($this->value), $this->encoding);
275
    }
276
277
    /**
278
     * Strip C-like slashes
279
     */
280 1
    public function stripCSlashes(): self
281
    {
282 1
        return new self(\stripcslashes($this->value), $this->encoding);
283
    }
284
285
    /**
286
     * Return the word count
287
     */
288 1
    public function wordCount(string $charlist = ''): int
289
    {
290 1
        return (int) \str_word_count(
291 1
            $this->value,
292 1
            0,
293 1
            $charlist
294
        );
295
    }
296
297
    /**
298
     * Return the collection of words
299
     *
300
     * @return Map<int, self>
301
     */
302 1
    public function words(string $charlist = ''): Map
303
    {
304
        /** @var list<string> */
305 1
        $words = \str_word_count($this->value, 2, $charlist);
306
        /** @var Map<int, self> */
307 1
        $map = Map::of('int', self::class);
308
309 1
        foreach ($words as $position => $word) {
310 1
            $map = ($map)($position, new self($word, $this->encoding));
311
        }
312
313 1
        return $map;
314
    }
315
316
    /**
317
     * Split the string using a regular expression
318
     *
319
     * @return Sequence<self>
320
     */
321 2
    public function pregSplit(string $regex, int $limit = -1): Sequence
322
    {
323 2
        $strings = \preg_split($regex, $this->value, $limit);
324
        /** @var Sequence<self> */
325 2
        $sequence = Sequence::of(self::class);
326
327 2
        foreach ($strings as $string) {
328 2
            $sequence = ($sequence)(new self($string, $this->encoding));
329
        }
330
331 2
        return $sequence;
332
    }
333
334
    /**
335
     * Check if the string match the given regular expression
336
     *
337
     * @throws RegexException If the regex failed
338
     */
339 2
    public function matches(string $regex): bool
340
    {
341 2
        return RegExp::of($regex)->matches($this);
342
    }
343
344
    /**
345
     * Return a collection of the elements matching the regex
346
     *
347
     * @throws RegexException If the regex failed
348
     *
349
     * @return Map<scalar, self>
350
     */
351 3
    public function capture(string $regex): Map
352
    {
353 3
        return RegExp::of($regex)->capture($this);
354
    }
355
356
    /**
357
     * Replace part of the string by using a regular expression
358
     *
359
     * @throws RegexException If the regex failed
360
     */
361 1
    public function pregReplace(
362
        string $regex,
363
        string $replacement,
364
        int $limit = -1
365
    ): self {
366 1
        $value = \preg_replace(
367 1
            $regex,
368 1
            $replacement,
369 1
            $this->value,
370 1
            $limit
371
        );
372
373 1
        if ($value === null) {
374
            throw new RegexException('', \preg_last_error());
375
        }
376
377 1
        return new self($value, $this->encoding);
378
    }
379
380
    /**
381
     * Return part of the string
382
     */
383 12
    public function substring(int $start, int $length = null): self
384
    {
385 12
        if ($this->empty()) {
386 1
            return $this;
387
        }
388
389 12
        $sub = \mb_substr($this->value, $start, $length, $this->encoding()->toString());
390
391 12
        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

391
        return new self($sub, /** @scrutinizer ignore-type */ $this->encoding);
Loading history...
392
    }
393
394 1
    public function take(int $size): self
395
    {
396 1
        return $this->substring(0, $size);
397
    }
398
399 2
    public function takeEnd(int $size): self
400
    {
401 2
        return $this->substring(-$size);
402
    }
403
404 2
    public function drop(int $size): self
405
    {
406 2
        return $this->substring($size);
407
    }
408
409 1
    public function dropEnd(int $size): self
410
    {
411 1
        return $this->substring(0, $this->length() - $size);
412
    }
413
414
    /**
415
     * Return a formatted string
416
     */
417 1
    public function sprintf(string ...$values): self
418
    {
419 1
        return new self(\sprintf($this->value, ...$values), $this->encoding);
420
    }
421
422
    /**
423
     * Return the string with the first letter as uppercase
424
     */
425 2
    public function ucfirst(): self
426
    {
427
        return $this
428 2
            ->substring(0, 1)
429 2
            ->toUpper()
430 2
            ->append($this->substring(1)->toString());
431
    }
432
433
    /**
434
     * Return the string with the first letter as lowercase
435
     */
436 2
    public function lcfirst(): self
437
    {
438
        return $this
439 2
            ->substring(0, 1)
440 2
            ->toLower()
441 2
            ->append($this->substring(1)->toString());
442
    }
443
444
    /**
445
     * Return a CamelCase representation of the string
446
     */
447 1
    public function camelize(): self
448
    {
449
        /**
450
         * @psalm-suppress InvalidArgument
451
         * @var Sequence<string>
452
         */
453
        $words = $this
454 1
            ->pregSplit('/_| /')
455
            ->map(function(self $part) {
456 1
                return $part->ucfirst();
457 1
            })
458 1
            ->toSequenceOf('string', fn($v) => yield $v->toString());
459
460 1
        return join('', $words)
0 ignored issues
show
Bug introduced by
$words of type Innmind\Immutable\Sequence is incompatible with the type Innmind\Immutable\Set expected by parameter $structure of Innmind\Immutable\join(). ( Ignorable by Annotation )

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

460
        return join('', /** @scrutinizer ignore-type */ $words)
Loading history...
461 1
            ->lcfirst()
462 1
            ->toEncoding($this->encoding()->toString());
463
    }
464
465
    /**
466
     * Append a string at the end of the current one
467
     */
468 4
    public function append(string $string): self
469
    {
470 4
        return new self($this->value.$string, $this->encoding);
471
    }
472
473
    /**
474
     * Prepend a string at the beginning of the current one
475
     */
476 1
    public function prepend(string $string): self
477
    {
478 1
        return new self($string.$this->value, $this->encoding);
479
    }
480
481
    /**
482
     * Check if the 2 strings are equal
483
     */
484 1
    public function equals(self $string): bool
485
    {
486 1
        return $this->toString() === $string->toString();
487
    }
488
489
    /**
490
     * Trim the string
491
     */
492 1
    public function trim(string $mask = null): self
493
    {
494 1
        return new self(
495 1
            $mask === null ? \trim($this->value) : \trim($this->value, $mask),
496 1
            $this->encoding
497
        );
498
    }
499
500
    /**
501
     * Trim the right side of the string
502
     */
503 1
    public function rightTrim(string $mask = null): self
504
    {
505 1
        return new self(
506 1
            $mask === null ? \rtrim($this->value) : \rtrim($this->value, $mask),
507 1
            $this->encoding
508
        );
509
    }
510
511
    /**
512
     * Trim the left side of the string
513
     */
514 1
    public function leftTrim(string $mask = null): self
515
    {
516 1
        return new self(
517 1
            $mask === null ? \ltrim($this->value) : \ltrim($this->value, $mask),
518 1
            $this->encoding
519
        );
520
    }
521
522
    /**
523
     * Check if the given string is present in the current one
524
     */
525 295
    public function contains(string $value): bool
526
    {
527
        try {
528 295
            $this->position($value);
529
530 13
            return true;
531 294
        } catch (SubstringException $e) {
532 294
            return false;
533
        }
534
    }
535
536
    /**
537
     * Check if the current string starts with the given string
538
     */
539 294
    public function startsWith(string $value): bool
540
    {
541 294
        if ($value === '') {
542 1
            return true;
543
        }
544
545
        try {
546 294
            return $this->position($value) === 0;
547 294
        } catch (SubstringException $e) {
548 294
            return false;
549
        }
550
    }
551
552
    /**
553
     * Check if the current string ends with the given string
554
     */
555 1
    public function endsWith(string $value): bool
556
    {
557 1
        if ($value === '') {
558 1
            return true;
559
        }
560
561 1
        return $this->takeEnd(self::of($value, $this->encoding)->length())->toString() === $value;
562
    }
563
564
    /**
565
     * Quote regular expression characters
566
     */
567 1
    public function pregQuote(string $delimiter = ''): self
568
    {
569 1
        return new self(\preg_quote($this->value, $delimiter), $this->encoding);
570
    }
571
572
    /**
573
     * Pad the string
574
     */
575 1
    private function pad(
576
        int $length,
577
        string $character = ' ',
578
        int $direction = self::PAD_RIGHT
579
    ): self {
580 1
        return new self(\str_pad(
581 1
            $this->value,
582 1
            $length,
583 1
            $character,
584 1
            $direction
585 1
        ), $this->encoding);
586
    }
587
}
588