Str   F
last analyzed

Complexity

Total Complexity 66

Size/Duplication

Total Lines 557
Duplicated Lines 0 %

Test Coverage

Coverage 97.44%

Importance

Changes 6
Bugs 0 Features 0
Metric Value
eloc 97
dl 0
loc 557
ccs 114
cts 117
cp 0.9744
rs 3.12
c 6
b 0
f 0
wmc 66

30 Methods

Rating   Name   Duplication   Size   Complexity  
A removeSpaces() 0 3 1
A upper() 0 3 1
A after() 0 3 2
A lower() 0 3 1
A finish() 0 5 1
A pregReplace() 0 3 1
A substr() 0 3 1
A length() 0 5 2
A camel() 0 7 2
A startsWith() 0 9 4
A convertToString() 0 3 2
A snake() 0 15 3
A e() 0 7 2
A before() 0 3 2
A match() 0 5 2
A isEmpty() 0 5 5
A doesntEmpty() 0 3 1
A contains() 0 9 4
A ascii() 0 3 1
A endsWith() 0 9 4
A random() 0 13 2
A slug() 0 19 3
A replace() 0 5 1
A of() 0 3 1
A studly() 0 11 2
A end() 0 5 1
A de() 0 3 1
B choice() 0 25 9
A start() 0 5 1
A title() 0 7 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
/*
3
 * This file is part of the "andrey-helldar/support" project.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @author Andrey Helldar <[email protected]>
9
 *
10
 * @copyright 2021 Andrey Helldar
11
 *
12
 * @license MIT
13
 *
14
 * @see https://github.com/andrey-helldar/support
15
 */
16
17
namespace Helldar\Support\Helpers;
18
19
use Helldar\Support\Facades\Helpers\Call as CallHelper;
20
use Helldar\Support\Facades\Tools\Replace;
21
use Illuminate\Contracts\Support\DeferringDisplayableValue;
0 ignored issues
show
Bug introduced by
The type Illuminate\Contracts\Sup...ferringDisplayableValue was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
22
use Illuminate\Contracts\Support\Htmlable;
0 ignored issues
show
Bug introduced by
The type Illuminate\Contracts\Support\Htmlable was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use voku\helper\ASCII;
24
25
class Str
26
{
27
    /**
28
     * The cache of snake-cased words.
29
     *
30
     * @var array
31
     */
32
    protected static $snakeCache = [];
33
34
    /**
35
     * The cache of camel-cased words.
36
     *
37
     * @var array
38
     */
39
    protected static $camelCache = [];
40
41
    /**
42
     * The cache of studly-cased words.
43
     *
44
     * @var array
45
     */
46
    protected static $studlyCache = [];
47
48
    protected $escaping_methods = [
49
        DeferringDisplayableValue::class => 'resolveDisplayableValue',
50
        Htmlable::class                  => 'toHtml',
51
    ];
52
53
    /**
54
     * Get a new stringable object from the given string.
55
     *
56
     * @see https://github.com/illuminate/support/blob/master/Str.php
57
     *
58
     * @param  string|null  $value
59
     *
60
     * @return \Helldar\Support\Helpers\Ables\Stringable
61
     */
62 195
    public function of(?string $value): Ables\Stringable
63
    {
64 195
        return new Ables\Stringable($value);
65
    }
66
67
    /**
68
     * Escape HTML special characters in a string.
69
     *
70
     * @param  string|null  $value
71
     * @param  bool  $double
72
     *
73
     * @return string|null
74
     */
75 2
    public function e(?string $value, bool $double = true): ?string
76
    {
77 2
        if ($escaped = CallHelper::runOf($this->escaping_methods, $value)) {
78
            return $escaped;
79
        }
80
81 2
        return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', $double);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type null; however, parameter $string of htmlspecialchars() 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

81
        return htmlspecialchars(/** @scrutinizer ignore-type */ $value, ENT_QUOTES, 'UTF-8', $double);
Loading history...
82
    }
83
84
    /**
85
     * Convert special HTML entities back to characters.
86
     *
87
     * @param  string|null  $value
88
     *
89
     * @return string|null
90
     */
91 2
    public function de(?string $value): ?string
92
    {
93 2
        return htmlspecialchars_decode($value, ENT_QUOTES);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type null; however, parameter $string of htmlspecialchars_decode() 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

93
        return htmlspecialchars_decode(/** @scrutinizer ignore-type */ $value, ENT_QUOTES);
Loading history...
94
    }
95
96
    /**
97
     * Replacing multiple spaces with a single space.
98
     *
99
     * @param  string|null  $value
100
     *
101
     * @return string|null
102
     */
103 6
    public function removeSpaces(?string $value): ?string
104
    {
105 6
        return $this->pregReplace($value, '!\s+!', ' ');
106
    }
107
108
    /**
109
     * Get a string according to an integer value.
110
     *
111
     * @param  float  $number
112
     * @param  array  $choice
113
     * @param  string|null  $extra
114
     *
115
     * @return string
116
     */
117 2
    public function choice(float $number, array $choice = [], string $extra = null): string
118
    {
119 2
        $number = (int) $number;
120 2
        $mod    = $number % 10;
121
122
        switch (true) {
123 2
            case $mod === 0:
124 2
            case $mod >= 5 && $mod <= 9:
125 2
            case ($number % 100 >= 11) && ($number % 100 <= 20):
126 2
                $result = $choice[2] ?? '';
127 2
                break;
128
129 2
            case $mod >= 2 && $mod <= 4:
130
                $result = $choice[1] ?? '';
131
                break;
132
133
            default:
134 2
                $result = $choice[0] ?? '';
135
        }
136
137 2
        if (empty($extra)) {
138 2
            return trim($result);
139
        }
140
141 2
        return implode(' ', [trim($result), trim($extra)]);
142
    }
143
144
    /**
145
     * Begin a string with a single instance of a given value.
146
     *
147
     * @see https://github.com/illuminate/support/blob/master/Str.php
148
     *
149
     * @param  string|null  $value
150
     * @param  string  $prefix
151
     *
152
     * @return string
153
     */
154 190
    public function start(?string $value, string $prefix): string
155
    {
156 190
        $quoted = preg_quote($prefix, '/');
157
158 190
        return $prefix . preg_replace('/^(?:' . $quoted . ')+/u', '', $value);
159
    }
160
161
    /**
162
     * End a string with a single instance of a given value.
163
     *
164
     * @param  string|null  $value
165
     * @param  string  $suffix
166
     *
167
     * @return string
168
     */
169 4
    public function end(?string $value, string $suffix): string
170
    {
171 4
        $quoted = preg_quote($suffix, '/');
172
173 4
        return preg_replace('/^(?:' . $quoted . ')+/u', '', $value) . $suffix;
174
    }
175
176
    /**
177
     * Cap a string with a single instance of a given value.
178
     *
179
     * @see https://github.com/illuminate/support/blob/master/Str.php
180
     *
181
     * @param  string  $value
182
     * @param  string  $cap
183
     *
184
     * @return string
185
     */
186 22
    public function finish(string $value, string $cap = '/'): string
187
    {
188 22
        $quoted = preg_quote($cap, '/');
189
190 22
        return preg_replace('/(?:' . $quoted . ')+$/u', '', $value) . $cap;
191
    }
192
193
    /**
194
     *  Determine if a given string starts with a given substring.
195
     *
196
     * @param  string  $haystack
197
     * @param  string|string[]  $needles
198
     *
199
     * @return bool
200
     */
201 16
    public function startsWith(string $haystack, $needles): bool
202
    {
203 16
        foreach ((array) $needles as $needle) {
204 16
            if ((string) $needle !== '' && str_starts_with($haystack, $needle)) {
205 16
                return true;
206
            }
207
        }
208
209 16
        return false;
210
    }
211
212
    /**
213
     * Determine if a given string ends with a given substring.
214
     *
215
     * @param  string  $haystack
216
     * @param  string|string[]  $needles
217
     *
218
     * @return bool
219
     */
220 6
    public function endsWith(string $haystack, $needles): bool
221
    {
222 6
        foreach ((array) $needles as $needle) {
223 6
            if ((string) $needle !== '' && str_ends_with($haystack, $needle)) {
224 6
                return true;
225
            }
226
        }
227
228 6
        return false;
229
    }
230
231
    /**
232
     * Convert the given string to lower-case.
233
     *
234
     * @see https://github.com/illuminate/support/blob/master/Str.php
235
     *
236
     * @param  string|null  $value
237
     *
238
     * @return string
239
     */
240 120
    public function lower(?string $value): string
241
    {
242 120
        return mb_strtolower($value, 'UTF-8');
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type null; however, parameter $string of mb_strtolower() 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

242
        return mb_strtolower(/** @scrutinizer ignore-type */ $value, 'UTF-8');
Loading history...
243
    }
244
245
    /**
246
     * Convert the given string to upper-case.
247
     *
248
     * @see https://github.com/illuminate/support/blob/master/Str.php
249
     *
250
     * @param  string|null  $value
251
     *
252
     * @return string
253
     */
254 9
    public function upper(?string $value): ?string
255
    {
256 9
        return mb_strtoupper($value, 'UTF-8');
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type null; however, parameter $string of mb_strtoupper() 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

256
        return mb_strtoupper(/** @scrutinizer ignore-type */ $value, 'UTF-8');
Loading history...
257
    }
258
259
    /**
260
     * Convert a value to studly caps case.
261
     *
262
     * @see https://github.com/illuminate/support/blob/master/Str.php
263
     *
264
     * @param  string|null  $value
265
     *
266
     * @return string|null
267
     */
268 53
    public function studly(?string $value): ?string
269
    {
270 53
        $key = $value;
271
272 53
        if (isset(self::$studlyCache[$key])) {
273 11
            return self::$studlyCache[$key];
274
        }
275
276 43
        $value = ucwords(str_replace(['-', '_'], ' ', $value));
277
278 43
        return self::$studlyCache[$key] = str_replace(' ', '', $value);
279
    }
280
281
    /**
282
     * Convert a value to camel case.
283
     *
284
     * @see https://github.com/illuminate/support/blob/master/Str.php
285
     *
286
     * @param  string|null  $value
287
     *
288
     * @return string|null
289
     */
290 220
    public function camel(?string $value): ?string
291
    {
292 220
        if (isset(self::$camelCache[$value])) {
293 180
            return self::$camelCache[$value];
294
        }
295
296 41
        return self::$camelCache[$value] = lcfirst($this->studly($value));
297
    }
298
299
    /**
300
     * Convert a string to snake case.
301
     *
302
     * @see https://github.com/illuminate/support/blob/master/Str.php
303
     *
304
     * @param  string|null  $value
305
     * @param  string  $delimiter
306
     *
307
     * @return string|null
308
     */
309 4
    public function snake(?string $value, string $delimiter = '_'): ?string
310
    {
311 4
        $key = $value;
312
313 4
        if (isset(self::$snakeCache[$key][$delimiter])) {
314 3
            return self::$snakeCache[$key][$delimiter];
315
        }
316
317 1
        if (! ctype_lower($value)) {
318 1
            $value = preg_replace('/\s+/u', '', ucwords($value));
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type null; however, parameter $string of ucwords() 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

318
            $value = preg_replace('/\s+/u', '', ucwords(/** @scrutinizer ignore-type */ $value));
Loading history...
319
320 1
            $value = $this->lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value));
321
        }
322
323 1
        return self::$snakeCache[$key][$delimiter] = $value;
324
    }
325
326
    /**
327
     * Generate a URL friendly "slug" from a given string.
328
     *
329
     * @see https://github.com/illuminate/support/blob/master/Str.php
330
     *
331
     * @param  string  $title
332
     * @param  string  $separator
333
     * @param  string|null  $language
334
     *
335
     * @return string
336
     */
337 8
    public function slug(string $title, string $separator = '-', ?string $language = 'en')
338
    {
339 8
        $title = $language ? $this->ascii($title, $language) : $title;
340
341
        // Convert all dashes/underscores into separator
342 8
        $flip = $separator === '-' ? '_' : '-';
343
344 8
        $title = preg_replace('![' . preg_quote($flip) . ']+!u', $separator, $title);
345
346
        // Replace @ with the word 'at'
347 8
        $title = str_replace('@', $separator . 'at' . $separator, $title);
348
349
        // Remove all characters that are not the separator, letters, numbers, or whitespace.
350 8
        $title = preg_replace('![^' . preg_quote($separator) . '\pL\pN\s]+!u', '', $this->lower($title));
351
352
        // Replace all separator characters and whitespace by a single separator
353 8
        $title = preg_replace('![' . preg_quote($separator) . '\s]+!u', $separator, $title);
354
355 8
        return trim($title, $separator);
356
    }
357
358
    /**
359
     * Convert the given string to title case.
360
     *
361
     * @see https://github.com/illuminate/support/blob/master/Str.php
362
     *
363
     * @param  string|null  $value
364
     *
365
     * @return string|null
366
     */
367 4
    public function title(?string $value): ?string
368
    {
369 4
        if (is_numeric($value)) {
370 4
            return $value;
371
        }
372
373 4
        return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8') ?: null;
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type null; however, parameter $string of mb_convert_case() 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

373
        return mb_convert_case(/** @scrutinizer ignore-type */ $value, MB_CASE_TITLE, 'UTF-8') ?: null;
Loading history...
374
    }
375
376
    /**
377
     * Return the length of the given string.
378
     *
379
     * @see https://github.com/illuminate/support/blob/master/Str.php
380
     *
381
     * @param  string|null  $value
382
     * @param  string|null  $encoding
383
     *
384
     * @return int
385
     */
386 2
    public function length(?string $value, string $encoding = null): int
387
    {
388 2
        return $encoding
389 2
            ? mb_strlen($value, $encoding)
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type null; however, parameter $string of mb_strlen() 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

389
            ? mb_strlen(/** @scrutinizer ignore-type */ $value, $encoding)
Loading history...
390 2
            : mb_strlen($value);
391
    }
392
393
    /**
394
     * Returns the portion of string specified by the start and length parameters.
395
     *
396
     * @see https://github.com/illuminate/support/blob/master/Str.php
397
     *
398
     * @param  string  $string
399
     * @param  int  $start
400
     * @param  int|null  $length
401
     *
402
     * @return string|null
403
     */
404 4
    public function substr(string $string, int $start, int $length = null): ?string
405
    {
406 4
        return mb_substr($string, $start, $length, 'UTF-8');
407
    }
408
409
    /**
410
     * Replace all occurrences of the search string with the replacement string.
411
     *
412
     * @param  string  $template
413
     * @param  array  $values
414
     * @param  string|null  $key_format
415
     *
416
     * @return string
417
     */
418 22
    public function replace(string $template, array $values, string $key_format = null): string
419
    {
420 22
        $keys = Replace::toFormatArray(array_keys($values), $key_format);
421
422 22
        return str_replace($keys, array_values($values), $template);
423
    }
424
425
    /**
426
     * Get the portion of a string before the first occurrence of a given value.
427
     *
428
     * @see https://github.com/illuminate/support/blob/master/Str.php
429
     *
430
     * @param  string  $subject
431
     * @param  string  $search
432
     *
433
     * @return string
434
     */
435 6
    public function before(string $subject, string $search): ?string
436
    {
437 6
        return ! empty($search) ? explode($search, $subject)[0] : null;
438
    }
439
440
    /**
441
     * Return the remainder of a string after the first occurrence of a given value.
442
     *
443
     * @see https://github.com/illuminate/support/blob/master/Str.php
444
     *
445
     * @param  string  $subject
446
     * @param  string  $search
447
     *
448
     * @return string
449
     */
450 10
    public function after(string $subject, string $search): ?string
451
    {
452 10
        return ! empty($search) ? array_reverse(explode($search, $subject, 2))[0] : null;
453
    }
454
455
    /**
456
     * Determine if a given string contains a given substring.
457
     *
458
     * @param  string  $haystack
459
     * @param  string|string[]  $needles
460
     *
461
     * @return bool
462
     */
463 73
    public function contains(string $haystack, $needles): bool
464
    {
465 73
        foreach ((array) $needles as $needle) {
466 73
            if ((string) $needle !== '' && str_contains($haystack, $needle)) {
467 72
                return true;
468
            }
469
        }
470
471 7
        return false;
472
    }
473
474
    /**
475
     * Generate a more truly "random" alpha-numeric string.
476
     *
477
     * @see https://github.com/illuminate/support/blob/master/Str.php
478
     *
479
     * @param  int  $length
480
     *
481
     * @throws \Exception
482
     *
483
     * @return string
484
     */
485 2
    public function random(int $length = 16): string
486
    {
487 2
        $string = '';
488
489 2
        while (($len = strlen($string)) < $length) {
490 2
            $size = $length - $len;
491
492 2
            $bytes = random_bytes($size);
493
494 2
            $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
495
        }
496
497 2
        return $string;
498
    }
499
500
    /**
501
     * Get the string matching the given pattern.
502
     *
503
     * @see https://github.com/illuminate/support/blob/master/Str.php
504
     *
505
     * @param  string  $value
506
     * @param  string  $pattern
507
     *
508
     * @return string|null
509
     */
510 4
    public function match(string $value, string $pattern): ?string
511
    {
512 4
        preg_match($pattern, $value, $matches);
513
514 4
        return ! $matches ? null : ($matches[1] ?? $matches[0]);
515
    }
516
517
    /**
518
     * Replace a given value in the string.
519
     *
520
     * @param  string|null  $value
521
     * @param  string  $pattern
522
     * @param  string  $replacement
523
     *
524
     * @return string|null
525
     */
526 10
    public function pregReplace(?string $value, string $pattern, string $replacement): ?string
527
    {
528 10
        return preg_replace($pattern, $replacement, $value);
529
    }
530
531
    /**
532
     * Determines if the value is empty.
533
     *
534
     * @param  mixed  $value
535
     *
536
     * @return bool
537
     */
538 8
    public function isEmpty($value): bool
539
    {
540 8
        $value = is_string($value) ? trim($value) : $value;
541
542 8
        return empty($value) && ! is_numeric($value) && (is_string($value) || is_null($value));
543
    }
544
545
    /**
546
     * Determines if the value is doesn't empty.
547
     *
548
     * @param  mixed  $value
549
     *
550
     * @return bool
551
     */
552 2
    public function doesntEmpty($value): bool
553
    {
554 2
        return ! $this->isEmpty($value);
555
    }
556
557
    /**
558
     * Transliterate a UTF-8 value to ASCII.
559
     *
560
     * @see https://github.com/illuminate/support/blob/master/Str.php
561
     *
562
     * @param  string|null  $value
563
     * @param  string|null  $language
564
     *
565
     * @return string
566
     */
567 12
    public function ascii(?string $value, ?string $language = 'en'): string
568
    {
569 12
        return ASCII::to_ascii((string) $value, $language);
0 ignored issues
show
Bug introduced by
It seems like $language can also be of type null; however, parameter $language of voku\helper\ASCII::to_ascii() 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

569
        return ASCII::to_ascii((string) $value, /** @scrutinizer ignore-type */ $language);
Loading history...
570
    }
571
572
    /**
573
     * Converts a value to a string.
574
     *
575
     * @param  string|null  $value
576
     *
577
     * @return string
578
     */
579 2
    public function convertToString(?string $value): string
580
    {
581 2
        return is_null($value) ? 'null' : $value;
582
    }
583
}
584