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 — master ( 5205fe...5906b8 )
by Baptiste
03:02
created

Str   F

Complexity

Total Complexity 64

Size/Duplication

Total Lines 684
Duplicated Lines 0 %

Test Coverage

Coverage 98.93%

Importance

Changes 0
Metric Value
eloc 158
dl 0
loc 684
ccs 185
cts 187
cp 0.9893
rs 3.28
c 0
b 0
f 0
wmc 64

45 Methods

Rating   Name   Duplication   Size   Complexity  
A prepend() 0 3 1
A length() 0 3 1
A pregQuote() 0 3 1
A repeat() 0 3 1
A toEncoding() 0 3 1
A toPrimitive() 0 3 1
A rightPad() 0 3 1
A append() 0 3 1
A of() 0 3 1
A empty() 0 3 1
A __toString() 0 3 1
A stripSlashes() 0 3 1
A leftPad() 0 3 1
A stripCSlashes() 0 3 1
A uniPad() 0 3 1
A toLower() 0 3 1
A toUpper() 0 3 1
A encoding() 0 3 1
A __construct() 0 4 1
A equals() 0 3 1
A sprintf() 0 3 1
A camelize() 0 9 1
A split() 0 14 4
A cspn() 0 14 2
A substring() 0 9 2
A wordCount() 0 6 1
A rightTrim() 0 5 2
A matches() 0 10 2
A leftTrim() 0 5 2
A ucfirst() 0 6 1
A getMatches() 0 6 1
A replace() 0 9 2
A pad() 0 11 1
A str() 0 12 2
A chunk() 0 11 2
A pregReplace() 0 17 2
A contains() 0 8 2
A reverse() 0 6 1
A pregSplit() 0 10 2
A shuffle() 0 6 1
A trim() 0 5 2
A words() 0 10 2
A lcfirst() 0 6 1
A position() 0 12 2
A capture() 0 24 3

How to fix   Complexity   

Complex Class

Complex classes like Str often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

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

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

1
<?php
2
declare(strict_types = 1);
3
4
namespace Innmind\Immutable;
5
6
use Innmind\Immutable\Exception\RegexException;
7
use Innmind\Immutable\Exception\SubstringException;
8
9
class Str implements PrimitiveInterface, StringableInterface
10
{
11
    const PAD_RIGHT = STR_PAD_RIGHT;
12
    const PAD_LEFT = STR_PAD_LEFT;
13
    const PAD_BOTH = STR_PAD_BOTH;
14
    const PREG_NO_FLAGS = 0;
15
    const PREG_SPLIT_NO_EMPTY = PREG_SPLIT_NO_EMPTY;
16
    const PREG_SPLIT_DELIM_CAPTURE = PREG_SPLIT_DELIM_CAPTURE;
17
    const PREG_SPLIT_OFFSET_CAPTURE = PREG_SPLIT_OFFSET_CAPTURE;
18
    const PREG_OFFSET_CAPTURE = PREG_OFFSET_CAPTURE;
19
20
    private $value;
21
    private $encoding;
22
23 322
    public function __construct(string $value, string $encoding = null)
24
    {
25 322
        $this->value = $value;
26 322
        $this->encoding = $encoding ?? \mb_internal_encoding();
27 322
    }
28
29 4
    public static function of(string $value, string $encoding = null): self
30
    {
31 4
        return new self($value, $encoding);
32
    }
33
34
    /**
35
     * {@inheritdoc}
36
     */
37 2
    public function toPrimitive()
38
    {
39 2
        return $this->value;
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45 204
    public function __toString(): string
46
    {
47 204
        return $this->value;
48
    }
49
50 6
    public function encoding(): self
51
    {
52 6
        return new self($this->encoding);
53
    }
54
55 30
    public function toEncoding(string $encoding): self
56
    {
57 30
        return new self($this->value, $encoding);
58
    }
59
60
    /**
61
     * Split the string into a collection of ones
62
     *
63
     * @param string $delimiter
64
     *
65
     * @return StreamInterface<self>
66
     */
67 14
    public function split(string $delimiter = null): StreamInterface
68
    {
69 14
        if (\is_null($delimiter) || $delimiter === '') {
70 6
            return $this->chunk();
71
        }
72
73 10
        $parts = \explode($delimiter, $this->value);
74 10
        $stream = new Stream(self::class);
75
76 10
        foreach ($parts as $part) {
77 10
            $stream = $stream->add(new self($part, $this->encoding));
78
        }
79
80 10
        return $stream;
81
    }
82
83
    /**
84
     * Returns a collection of the string splitted by the given chunk size
85
     *
86
     * @param int $size
87
     *
88
     * @return StreamInterface<self>
89
     */
90 14
    public function chunk(int $size = 1): StreamInterface
91
    {
92 14
        $stream = new Stream(self::class);
93 14
        $string = $this;
94
95 14
        while ($string->length() > 0) {
96 14
            $stream = $stream->add($string->substring(0, $size));
97 14
            $string = $string->substring($size);
98
        }
99
100 14
        return $stream;
101
    }
102
103
    /**
104
     * Returns the position of the first occurence of the string
105
     *
106
     * @param string $needle
107
     * @param int $offset
108
     *
109
     * @throws SubstringException If the string is not found
110
     *
111
     * @return int
112
     */
113 12
    public function position(string $needle, int $offset = 0): int
114
    {
115 12
        $position = \mb_strpos($this->value, $needle, $offset, $this->encoding);
116
117 12
        if ($position === false) {
118 6
            throw new SubstringException(\sprintf(
119 6
                'Substring "%s" not found',
120 6
                $needle
121
            ));
122
        }
123
124 10
        return (int) $position;
125
    }
126
127
    /**
128
     * Replace all occurences of the search string with the replacement one
129
     *
130
     * @param string $search
131
     * @param string $replacement
132
     *
133
     * @return self
134
     */
135 4
    public function replace(string $search, string $replacement): self
136
    {
137 4
        if (!$this->contains($search)) {
138 2
            return $this;
139
        }
140
141
        return $this
142 4
            ->split($search)
143 4
            ->join($replacement);
144
    }
145
146
    /**
147
     * Returns the string following the given delimiter
148
     *
149
     * @param string $delimiter
150
     *
151
     * @throws SubstringException If the string is not found
152
     *
153
     * @return self
154
     */
155 6
    public function str(string $delimiter): self
156
    {
157 6
        $sub = \mb_strstr($this->value, $delimiter, false, $this->encoding);
158
159 6
        if ($sub === false) {
160 2
            throw new SubstringException(\sprintf(
161 2
                'Substring "%s" not found',
162 2
                $delimiter
163
            ));
164
        }
165
166 4
        return new self($sub, $this->encoding);
167
    }
168
169
    /**
170
     * Return the string in upper case
171
     *
172
     * @return self
173
     */
174 6
    public function toUpper(): self
175
    {
176 6
        return new self(\mb_strtoupper($this->value), $this->encoding);
177
    }
178
179
    /**
180
     * Return the string in lower case
181
     *
182
     * @return self
183
     */
184 4
    public function toLower(): self
185
    {
186 4
        return new self(\mb_strtolower($this->value), $this->encoding);
187
    }
188
189
    /**
190
     * Return the string length
191
     *
192
     * @return int
193
     */
194 30
    public function length(): int
195
    {
196 30
        return \mb_strlen($this->value, $this->encoding);
197
    }
198
199 2
    public function empty(): bool
200
    {
201 2
        return $this->value === '';
202
    }
203
204
    /**
205
     * Reverse the string
206
     *
207
     * @return self
208
     */
209 2
    public function reverse(): self
210
    {
211
        return $this
212 2
            ->chunk()
213 2
            ->reverse()
214 2
            ->join('');
215
    }
216
217
    /**
218
     * Pad to the right
219
     *
220
     * @param int $length
221
     * @param string $character
222
     *
223
     * @return self
224
     */
225 2
    public function rightPad(int $length, string $character = ' '): self
226
    {
227 2
        return $this->pad($length, $character, self::PAD_RIGHT);
228
    }
229
230
    /**
231
     * Pad to the left
232
     *
233
     * @param int $length
234
     * @param string $character
235
     *
236
     * @return self
237
     */
238 2
    public function leftPad(int $length, string $character = ' '): self
239
    {
240 2
        return $this->pad($length, $character, self::PAD_LEFT);
241
    }
242
243
    /**
244
     * Pad both sides
245
     *
246
     * @param int $length
247
     * @param string $character
248
     *
249
     * @return self
250
     */
251 2
    public function uniPad(int $length, string $character = ' '): self
252
    {
253 2
        return $this->pad($length, $character, self::PAD_BOTH);
254
    }
255
256
    /**
257
     * Find length of initial segment not matching mask
258
     *
259
     * @param string $mask
260
     * @param int $start
261
     * @param int $length
262
     *
263
     * @return int
264
     */
265 2
    public function cspn(string $mask, int $start = 0, int $length = null): int
266
    {
267 2
        if ($length === null) {
268 2
            $value = \strcspn($this->value, $mask, $start);
269
        } else {
270 2
            $value = \strcspn(
271 2
                $this->value,
272 2
                $mask,
273 2
                $start,
274 2
                $length
275
            );
276
        }
277
278 2
        return (int) $value;
279
    }
280
281
    /**
282
     * Repeat the string n times
283
     *
284
     * @param int $repeat
285
     *
286
     * @return self
287
     */
288 2
    public function repeat(int $repeat): self
289
    {
290 2
        return new self(\str_repeat($this->value, $repeat), $this->encoding);
291
    }
292
293
    /**
294
     * Shuffle the string
295
     *
296
     * @return self
297
     */
298 4
    public function shuffle(): self
299
    {
300 4
        $parts = $this->chunk()->toPrimitive();
301 4
        \shuffle($parts);
302
303 4
        return new self(\implode('', $parts), $this->encoding);
304
    }
305
306
    /**
307
     * Strip slashes
308
     *
309
     * @return self
310
     */
311 2
    public function stripSlashes(): self
312
    {
313 2
        return new self(\stripslashes($this->value), $this->encoding);
314
    }
315
316
    /**
317
     * Strip C-like slashes
318
     *
319
     * @return self
320
     */
321 2
    public function stripCSlashes(): self
322
    {
323 2
        return new self(\stripcslashes($this->value), $this->encoding);
324
    }
325
326
    /**
327
     * Return the word count
328
     *
329
     * @param string $charlist
330
     *
331
     * @return int
332
     */
333 2
    public function wordCount(string $charlist = ''): int
334
    {
335 2
        return (int) \str_word_count(
336 2
            $this->value,
337 2
            0,
338 2
            $charlist
339
        );
340
    }
341
342
    /**
343
     * Return the collection of words
344
     *
345
     * @param string $charlist
346
     *
347
     * @return MapInterface<int, self>
348
     */
349 2
    public function words(string $charlist = ''): MapInterface
350
    {
351 2
        $words = \str_word_count($this->value, 2, $charlist);
352 2
        $map = new Map('int', self::class);
353
354 2
        foreach ($words as $position => $word) {
355 2
            $map = $map->put($position, new self($word, $this->encoding));
356
        }
357
358 2
        return $map;
359
    }
360
361
    /**
362
     * Split the string using a regular expression
363
     *
364
     * @param string $regex
365
     * @param int $limit
366
     *
367
     * @return StreamInterface<self>
368
     */
369 4
    public function pregSplit(string $regex, int $limit = -1): StreamInterface
370
    {
371 4
        $strings = \preg_split($regex, $this->value, $limit);
372 4
        $stream = new Stream(self::class);
373
374 4
        foreach ($strings as $string) {
375 4
            $stream = $stream->add(new self($string, $this->encoding));
376
        }
377
378 4
        return $stream;
379
    }
380
381
    /**
382
     * Check if the string match the given regular expression
383
     *
384
     * @param string $regex
385
     * @param int $offset
386
     *
387
     * @throws Exception If the regex failed
388
     *
389
     * @return bool
390
     */
391 4
    public function matches(string $regex, int $offset = 0): bool
392
    {
393 4
        $matches = [];
394 4
        $value = \preg_match($regex, $this->value, $matches, 0, $offset);
395
396 4
        if ($value === false) {
397 2
            throw new RegexException('', \preg_last_error());
398
        }
399
400 2
        return (bool) $value;
401
    }
402
403
    /**
404
     * Return a collection of the elements matching the regex
405
     *
406
     * @deprecated replaced by self::capture, to be removed in 3.0
407
     *
408
     * @param string $regex
409
     * @param int $offset
410
     * @param int $flags
411
     *
412
     * @throws Exception If the regex failed
413
     *
414
     * @return MapInterface<scalar, self>
415
     */
416 2
    public function getMatches(
417
        string $regex,
418
        int $offset = 0,
419
        int $flags = self::PREG_NO_FLAGS
420
    ): MapInterface {
421 2
        return $this->capture($regex, $offset, $flags);
422
    }
423
424
    /**
425
     * Return a collection of the elements matching the regex
426
     *
427
     * @param string $regex
428
     * @param int $offset
429
     * @param int $flags
430
     *
431
     * @throws Exception If the regex failed
432
     *
433
     * @return MapInterface<scalar, self>
434
     */
435 6
    public function capture(
436
        string $regex,
437
        int $offset = 0,
438
        int $flags = self::PREG_NO_FLAGS
439
    ): MapInterface {
440 6
        $matches = [];
441 6
        $value = \preg_match(
442 6
            $regex,
443 6
            $this->value,
444
            $matches,
445 6
            $flags,
446 6
            $offset
447
        );
448 6
        $map = new Map('scalar', self::class);
449
450 6
        foreach ($matches as $key => $match) {
451 4
            $map = $map->put($key, new self((string) $match, $this->encoding));
452
        }
453
454 6
        if ($value === false) {
455 2
            throw new RegexException('', \preg_last_error());
456
        }
457
458 4
        return $map;
459
    }
460
461
    /**
462
     * Replace part of the string by using a regular expression
463
     *
464
     * @param string $regex
465
     * @param string $replacement
466
     * @param int $limit
467
     *
468
     * @throws Exception If the regex failed
469
     *
470
     * @return self
471
     */
472 2
    public function pregReplace(
473
        string $regex,
474
        string $replacement,
475
        int $limit = -1
476
    ): self {
477 2
        $value = \preg_replace(
478 2
            $regex,
479 2
            $replacement,
480 2
            $this->value,
481 2
            $limit
482
        );
483
484 2
        if ($value === null) {
485
            throw new RegexException('', \preg_last_error());
486
        }
487
488 2
        return new self($value, $this->encoding);
489
    }
490
491
    /**
492
     * Return part of the string
493
     *
494
     * @param int $start
495
     * @param int $length
496
     *
497
     * @return self
498
     */
499 26
    public function substring(int $start, int $length = null): self
500
    {
501 26
        if ($this->length() === 0) {
502 2
            return $this;
503
        }
504
505 26
        $sub = \mb_substr($this->value, $start, $length, $this->encoding);
506
507 26
        return new self($sub, $this->encoding);
508
    }
509
510
    /**
511
     * Return a formatted string
512
     *
513
     * @return self
514
     */
515 2
    public function sprintf(...$values): self
516
    {
517 2
        return new self(\sprintf($this->value, ...$values), $this->encoding);
518
    }
519
520
    /**
521
     * Return the string with the first letter as uppercase
522
     *
523
     * @return self
524
     */
525 4
    public function ucfirst(): self
526
    {
527
        return $this
528 4
            ->substring(0, 1)
529 4
            ->toUpper()
530 4
            ->append((string) $this->substring(1));
531
    }
532
533
    /**
534
     * Return the string with the first letter as lowercase
535
     *
536
     * @return self
537
     */
538 2
    public function lcfirst(): self
539
    {
540
        return $this
541 2
            ->substring(0, 1)
542 2
            ->toLower()
543 2
            ->append((string) $this->substring(1));
544
    }
545
546
    /**
547
     * Return a CamelCase representation of the string
548
     *
549
     * @return self
550
     */
551 2
    public function camelize(): self
552
    {
553
        return $this
554 2
            ->pregSplit('/_| /')
555 2
            ->map(function(self $part) {
556 2
                return $part->ucfirst();
557 2
            })
558 2
            ->join('')
559 2
            ->toEncoding($this->encoding);
560
    }
561
562
    /**
563
     * Append a string at the end of the current one
564
     *
565
     * @param string $string
566
     *
567
     * @return self
568
     */
569 8
    public function append(string $string): self
570
    {
571 8
        return new self((string) $this.$string, $this->encoding);
572
    }
573
574
    /**
575
     * Prepend a string at the beginning of the current one
576
     *
577
     * @param string $string
578
     *
579
     * @return self
580
     */
581 2
    public function prepend(string $string): self
582
    {
583 2
        return new self($string.(string) $this, $this->encoding);
584
    }
585
586
    /**
587
     * Check if the 2 strings are equal
588
     *
589
     * @param self $string
590
     *
591
     * @return bool
592
     */
593 62
    public function equals(self $string): bool
594
    {
595 62
        return (string) $this === (string) $string;
596
    }
597
598
    /**
599
     * Trim the string
600
     *
601
     * @param string $mask
602
     *
603
     * @return self
604
     */
605 2
    public function trim(string $mask = null): self
606
    {
607 2
        return new self(
608 2
            $mask === null ? \trim((string) $this) : \trim((string) $this, $mask),
609 2
            $this->encoding
610
        );
611
    }
612
613
    /**
614
     * Trim the right side of the string
615
     *
616
     * @param string $mask
617
     *
618
     * @return self
619
     */
620 2
    public function rightTrim(string $mask = null): self
621
    {
622 2
        return new self(
623 2
            $mask === null ? \rtrim((string) $this) : \rtrim((string) $this, $mask),
624 2
            $this->encoding
625
        );
626
    }
627
628
    /**
629
     * Trim the left side of the string
630
     *
631
     * @param string $mask
632
     *
633
     * @return self
634
     */
635 2
    public function leftTrim(string $mask = null): self
636
    {
637 2
        return new self(
638 2
            $mask === null ? \ltrim((string) $this) : \ltrim((string) $this, $mask),
639 2
            $this->encoding
640
        );
641
    }
642
643
    /**
644
     * Check if the given string is present in the current one
645
     *
646
     * @param string $value
647
     *
648
     * @return bool
649
     */
650 6
    public function contains(string $value): bool
651
    {
652
        try {
653 6
            $this->position($value);
654
655 6
            return true;
656 4
        } catch (SubstringException $e) {
657 4
            return false;
658
        }
659
    }
660
661
    /**
662
     * Quote regular expression characters
663
     *
664
     * @param string $delimiter
665
     *
666
     * @return self
667
     */
668 2
    public function pregQuote(string $delimiter = ''): self
669
    {
670 2
        return new self(\preg_quote((string) $this, $delimiter), $this->encoding);
671
    }
672
673
    /**
674
     * Pad the string
675
     *
676
     * @param int $length
677
     * @param string $character
678
     * @param int $direction
679
     *
680
     * @return self
681
     */
682 2
    private function pad(
683
        int $length,
684
        string $character = ' ',
685
        int $direction = self::PAD_RIGHT
686
    ): self {
687 2
        return new self(\str_pad(
688 2
            $this->value,
689 2
            $length,
690 2
            $character,
691 2
            $direction
692 2
        ), $this->encoding);
693
    }
694
}
695