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.

StringsMethods::toSnakeCase()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
declare(strict_types=1);
3
4
/*
5
 * This file is part of Underscore.php
6
 *
7
 * (c) Maxime Fabre <[email protected]>
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace Underscore\Methods;
14
15
use Doctrine\Inflector\CachedWordInflector;
16
use Doctrine\Inflector\Inflector;
17
use Doctrine\Inflector\Rules\English\Rules;
18
use Doctrine\Inflector\RulesetInflector;
19
use Exception;
20
use RuntimeException;
21
use Underscore\Types\Strings;
22
use function Symfony\Component\String\u;
23
24
/**
25
 * Methods to manage strings.
26
 */
27
class StringsMethods
28
{
29
30
    /**
31
     * Uncountable word forms.
32
     */
33
    public static array $uncountable = [
34
        'audio',
35
        'bison',
36
        'cattle',
37
        'chassis',
38
        'compensation',
39
        'coreopsis',
40
        'data',
41
        'deer',
42
        'education',
43
        'emoji',
44
        'equipment',
45
        'evidence',
46
        'feedback',
47
        'firmware',
48
        'fish',
49
        'furniture',
50
        'gold',
51
        'hardware',
52
        'information',
53
        'jedi',
54
        'kin',
55
        'knowledge',
56
        'love',
57
        'metadata',
58
        'money',
59
        'moose',
60
        'news',
61
        'nutrition',
62
        'offspring',
63
        'plankton',
64
        'pokemon',
65
        'police',
66
        'rain',
67
        'recommended',
68
        'related',
69
        'rice',
70
        'series',
71
        'sheep',
72
        'software',
73
        'species',
74
        'swine',
75
        'traffic',
76
        'wheat',
77
    ];
78
79
    ////////////////////////////////////////////////////////////////////
80
    ////////////////////////////// CREATE  /////////////////////////////
81
    ////////////////////////////////////////////////////////////////////
82
83
    /**
84
     * Create a string from a number.
85
     *
86
     * @param  int  $count  A number
87
     * @param  string  $many  If many
88
     * @param  string  $one  If one
89
     * @param  string|null  $zero  If one
90
     *
91
     * @return string A string
92
     */
93 3
    public static function accord(int $count, string $many, string $one, string $zero = null) : string
94
    {
95 3
        if ($count === 1) {
96 1
            $output = $one;
97
        } elseif ($count === 0 && ! empty($zero)) {
98 2
            $output = $zero;
99 1
        } else {
100
            $output = $many;
101
        }
102 1
103
        return sprintf($output, $count);
104
    }
105 3
106
    /**
107
     * Generate a more truly "random" alpha-numeric string.
108
     *
109
     * @author Taylor Otwell
110
     *
111
     *
112
     */
113
    public static function random(int $length = 16) : string
114
    {
115
        if (\function_exists('random_bytes')) {
116
            try {
117
                $bytes = \random_bytes($length * 2);
118
            } catch (Exception) {
119 10
                throw new RuntimeException('Unable to generate random string.');
120
            }
121 10
122 10
            if ($bytes === '') {
123
                throw new RuntimeException('Unable to generate random string.');
124 10
            }
125
126
            return substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $length);
127
        }
128 10
129
        return static::quickRandom($length);
130
    }
131
132
    /**
133
     * Generate a "random" alpha-numeric string.
134
     * Should not be considered sufficient for cryptography, etc.
135
     *
136
     * @author Taylor Otwell
137
     *
138
     *
139
     */
140
    public static function quickRandom(int $length = 16) : string
141
    {
142
        $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
143
144
        return substr(str_shuffle(str_repeat($pool, $length)), 0, $length);
145
    }
146
147
    /**
148
     * Generates a random suite of words.
149
     *
150
     * @param  int  $words  The number of words
151
     * @param  int  $length  The length of each word
152
     */
153
    public static function randomStrings(int $words, int $length = 10) : string
154
    {
155
        return Strings::from('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
156
                      ->shuffle()
0 ignored issues
show
Bug introduced by
The method shuffle() does not exist on Underscore\Types\Strings. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

156
                      ->/** @scrutinizer ignore-call */ shuffle()
Loading history...
157
                      ->split($length)
0 ignored issues
show
Bug introduced by
The method split() does not exist on Underscore\Types\Strings. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

157
                      ->/** @scrutinizer ignore-call */ split($length)
Loading history...
158
                      ->slice(0, $words)
0 ignored issues
show
Bug introduced by
The method slice() does not exist on Underscore\Types\Strings. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

158
                      ->/** @scrutinizer ignore-call */ slice(0, $words)
Loading history...
159
                      ->implode(' ')
0 ignored issues
show
Bug introduced by
The method implode() does not exist on Underscore\Types\Strings. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

159
                      ->/** @scrutinizer ignore-call */ implode(' ')
Loading history...
160 1
                      ->obtain();
161
    }
162 1
163 1
    ////////////////////////////////////////////////////////////////////
164 1
    ////////////////////////////// ANALYZE /////////////////////////////
165 1
    ////////////////////////////////////////////////////////////////////
166 1
    /**
167 1
     * Determine if a given string ends with a given substring.
168
     *
169
     * @author Taylor Otwell
170
     *
171
     *
172
     */
173
    public static function endsWith(string $haystack, array|string $needles) : bool
174
    {
175
        foreach ((array) $needles as $needle) {
176
            if (str_ends_with($haystack, (string) $needle)) {
177
                return true;
178
            }
179
        }
180
181
        return false;
182
    }
183
184 1
    /**
185
     * Check if a string is an IP.
186 1
     *
187 1
     * @param $string
188 1
     */
189
    public static function isIp($string) : bool
190
    {
191
        return filter_var($string, FILTER_VALIDATE_IP) !== false;
192 1
    }
193
194
    /**
195
     * Check if a string is an email.
196
     *
197
     * @param $string
198
     */
199
    public static function isEmail($string) : bool
200
    {
201
        return filter_var($string, FILTER_VALIDATE_EMAIL) !== false;
202 1
    }
203
204 1
    /**
205
     * Check if a string is an url.
206
     *
207
     * @param $string
208
     */
209
    public static function isUrl($string) : bool
210
    {
211
        return filter_var($string, FILTER_VALIDATE_URL) !== false;
212
    }
213
214 1
    /**
215
     * Determine if a given string starts with a given substring.
216 1
     *
217
     * @author Taylor Otwell
218
     *
219
     *
220
     */
221
    public static function startsWith(string $haystack, array|string $needles) : bool
222
    {
223
        foreach ((array) $needles as $needle) {
224
            if ($needle !== '' && str_starts_with($haystack, (string) $needle)) {
225
                return true;
226 1
            }
227
        }
228 1
229
        return false;
230
    }
231
232
    ////////////////////////////////////////////////////////////////////
233
    ///////////////////////////// FETCH FROM ///////////////////////////
234
    ////////////////////////////////////////////////////////////////////
235
236
    /**
237
     * Find one or more needles in one or more haystacks.
238
     *
239
     * @param  array|string  $string  The haystack(s) to search in
240
     * @param  array|string  $needle  The needle(s) to search for
241 1
     * @param  bool  $caseSensitive  Whether the function is case sensitive or not
242
     * @param  bool  $absolute  Whether all needle need to be found or whether one is enough
243 1
     *
244 1
     * @return bool Found or not
245 1
     */
246
    public static function find(
247
        array|string $string,
248
        array|string $needle,
249 1
        bool $caseSensitive = false,
250
        bool $absolute = false
251
    ) : bool
252
    {
253
        // If several needles
254
        if (\is_array($needle) || \is_array($string)) {
0 ignored issues
show
introduced by
The condition is_array($needle) is always true.
Loading history...
255
            $sliceFrom = $string;
256
            $sliceTo   = $needle;
257
258
            if (\is_array($needle)) {
259
                $sliceFrom = $needle;
260
                $sliceTo   = $string;
261
            }
262
263
            $found = 0;
264
            foreach ($sliceFrom as $need) {
265
                if (static::find($sliceTo, $need, $absolute, $caseSensitive)) {
266 163
                    ++$found;
267
                }
268
            }
269 163
270 9
            return ($absolute) ? \count($sliceFrom) === $found : $found > 0;
271 9
        }
272
273 9
        // If not case sensitive
274 4
        if (!$caseSensitive) {
275 4
            $string = strtolower($string);
276
            $needle = strtolower($needle);
277
        }
278 9
279 9
        // If string found
280 9
        $pos = strpos($string, $needle);
281 7
282
        return $pos !== false;
283
    }
284
285 9
    /**
286
     * Slice a string with another string.
287
     *
288
     * @param $string
289 163
     * @param $slice
290 163
     */
291 163
    public static function slice($string, $slice) : array
292
    {
293
        $sliceTo   = static::sliceTo($string, $slice);
294
        $sliceFrom = static::sliceFrom($string, $slice);
295 163
296
        return [$sliceTo, $sliceFrom];
297 163
    }
298
299
    /**
300
     * Slice a string from a certain point.
301
     *
302
     * @param $string
303
     * @param $slice
304
     *
305
     * @return false|string
306
     */
307
    public static function sliceFrom($string, $slice) : bool|string
308 1
    {
309
        $slice = strpos((string) $string, (string) $slice);
310 1
311 1
        return substr((string) $string, $slice);
312
    }
313 1
314
    /**
315
     * Slice a string up to a certain point.
316
     *
317
     * @param $string
318
     * @param $slice
319
     *
320
     * @return false|string
321
     */
322
    public static function sliceTo($string, $slice) : bool|string
323
    {
324 2
        $slice = strpos((string) $string, (string) $slice);
325
326 2
        return substr((string) $string, 0, $slice);
327
    }
328 2
329
    /**
330
     * Get the base class in a namespace.
331
     *
332
     *
333
     */
334
    public static function baseClass(string $string) : string
335
    {
336
        $path = static::replace($string, '\\', '/');
337
338
        return basename($path);
339 2
    }
340
341 2
    ////////////////////////////////////////////////////////////////////
342
    /////////////////////////////// ALTER //////////////////////////////
343 2
    ////////////////////////////////////////////////////////////////////
344
    /**
345
     * Prepend a string with another.
346
     *
347
     * @param  string  $string  The string
348
     * @param  string  $with  What to prepend with
349
     */
350
    public static function prepend(string $string, string $with) : string
351
    {
352
        return $with.$string;
353 1
    }
354
355 1
    /**
356
     * Append a string to another.
357 1
     *
358
     * @param  string  $string  The string
359
     * @param  string  $with  What to append with
360
     */
361
    public static function append(string $string, string $with) : string
362
    {
363
        return $string.$with;
364
    }
365
366
    /**
367
     * Limit the number of characters in a string.
368
     *
369
     * @author Taylor Otwell
370
     *
371
     *
372 1
     */
373
    public static function limit(string $value, int $limit = 100, string $end = '...') : string
374 1
    {
375
        if (mb_strlen($value) <= $limit) {
376
            return $value;
377
        }
378
379
        return rtrim(mb_substr($value, 0, $limit, 'UTF-8')).$end;
380
    }
381
382
    /**
383
     * Remove part of a string.
384
     *
385 1
     * @param $string
386
     * @param $remove
387 1
     */
388
    public static function remove($string, $remove) : string
389
    {
390
        if (\is_array($remove)) {
391
            $string = preg_replace('#('.implode('|', $remove).')#', '', (string) $string);
392
        }
393
394
        // Trim and return
395
        return trim(str_replace($remove, '', $string));
396
    }
397
398
    /**
399
     * Correct arguments order for str_replace.
400
     *
401 2
     * @param $string
402
     * @param $replace
403 2
     * @param $with
404 1
     *
405
     * @return string|string[]
406
     */
407 2
    public static function replace($string, $replace, $with) : array|string
408
    {
409
        return str_replace($replace, $with, $string);
410
    }
411
412
    /**
413
     * Toggles a string between two states.
414
     *
415
     * @param  string  $string  The string to toggle
416
     * @param  string  $first  First value
417
     * @param  string  $second  Second value
418 2
     * @param  bool  $loose  Whether a string neither matching 1 or 2 should be changed
419
     *
420 2
     * @return string The toggled string
421 1
     */
422
    public static function toggle(string $string, string $first, string $second, bool $loose = false) : string
423
    {
424
        // If the string given match none of the other two, and we're in strict mode, return it
425 2
        if ( ! $loose && ! \in_array($string, [$first, $second], true)) {
426
            return $string;
427
        }
428
429
        return $string === $first ? $second : $first;
430
    }
431
432
    /**
433
     * Generate a URL friendly "slug" from a given string.
434
     *
435
     * @author Taylor Otwell
436
     *
437 3
     *
438
     */
439 3
    protected static function slug(string $title, string $separator = '-') : string
440
    {
441
        $title = u($title)->ascii()->toString();
442
        // Convert all dashes/underscores into separator
443
        $flip = $separator === '-' ? '_' : '-';
444
445
        $title = preg_replace('!['.preg_quote($flip).']+!u', $separator, $title);
446
447
        // Remove all characters that are not the separator, letters, numbers, or whitespace.
448
        $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', mb_strtolower((string) $title));
449
450
        // Replace all separator characters and whitespace by a single separator
451
        $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, (string) $title);
452 3
453
        return trim((string) $title, $separator);
454
    }
455 3
456 1
    /**
457
     * Slugifies a string.
458
     *
459 2
     * @param        $string
460
     *
461
     */
462
    public static function slugify($string, string $separator = '-') : string
463
    {
464
        $string = str_replace('_', ' ', $string);
465
466
        return static::slug($string, $separator);
467
    }
468
469
    /**
470
     * Explode a string into an array.
471
     *
472 1
     * @param      $string
473
     * @param      $with
474 1
     *
475
     */
476 1
    public static function explode($string, $with, $limit = null) : array
477
    {
478 1
        if ( ! $limit) {
479
            return explode($with, (string) $string);
480
        }
481 1
482
        return explode($with, (string) $string, $limit);
483
    }
484 1
485
    /**
486 1
     * Lowercase a string.
487
     *
488
     *
489
     */
490
    public static function lower(string $string) : string
491
    {
492
        return mb_strtolower($string, 'UTF-8');
493
    }
494
495
    /**
496
     * Get the plural form of an English word.
497 1
     *
498
     *
499 1
     * @return string
500
     */
501 1
    public static function plural(string $value = null) : ?string
502
    {
503
        if (static::uncountable($value)) {
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type null; however, parameter $value of Underscore\Methods\StringsMethods::uncountable() 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

503
        if (static::uncountable(/** @scrutinizer ignore-type */ $value)) {
Loading history...
504
            return $value;
505
        }
506
507
        $plural = static::inflector()->pluralize($value);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type null; however, parameter $word of Doctrine\Inflector\Inflector::pluralize() 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

507
        $plural = static::inflector()->pluralize(/** @scrutinizer ignore-type */ $value);
Loading history...
508
509
        return static::matchCase($plural, $value);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type null; however, parameter $comparison of Underscore\Methods\StringsMethods::matchCase() 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

509
        return static::matchCase($plural, /** @scrutinizer ignore-type */ $value);
Loading history...
510
    }
511
512
    /**
513 2
     * Get the singular form of an English word.
514
     *
515 2
     *
516 2
     */
517
    public static function singular(string $value) : string
518
    {
519 1
        $singular = static::inflector()->singularize($value);
520
521
        return static::matchCase($singular, $value);
522
    }
523
524
    /**
525
     * Lowercase a string.
526
     *
527
     *
528
     */
529 2
    public static function upper(string $string) : string
530
    {
531 2
        return mb_strtoupper($string, 'UTF-8');
532
    }
533
534
    /**
535
     * Convert a string to title case.
536
     *
537
     *
538
     */
539
    public static function title(string $string) : string
540
    {
541
        return mb_convert_case($string, MB_CASE_TITLE, 'UTF-8');
542 1
    }
543
544 1
    /**
545
     * Limit the number of words in a string.
546
     *
547
     * @author Taylor Otwell
548 1
     *
549
     *
550 1
     */
551
    public static function words(string $value, int $words = 100, string $end = '...') : string
552
    {
553
        preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches);
554
555
        if ( ! \array_key_exists(0, $matches) || \strlen($value) === \strlen($matches[0])) {
556
            return $value;
557
        }
558
559
        return rtrim($matches[0]).$end;
560
    }
561
562
    ////////////////////////////////////////////////////////////////////
563
    /////////////////////////// CASE SWITCHERS /////////////////////////
564
    ////////////////////////////////////////////////////////////////////
565
    /**
566
     * Convert a string to PascalCase.
567
     *
568
     *
569
     */
570
    public static function toPascalCase(string $string) : string
571
    {
572
        return u($string)->camel()->title()->toString();
573 1
    }
574
575 1
    /**
576
     * Convert a string to snake_case.
577
     *
578
     *
579
     */
580
    public static function toSnakeCase(string $string) : string
581
    {
582
        return u($string)->snake()->toString();
583
    }
584
585 2
    /**
586
     * Convert a string to camelCase.
587 2
     *
588
     *
589
     */
590
    public static function toCamelCase(string $string) : string
591
    {
592
        return u($string)->camel()->toString();
593
    }
594
595
    /**
596
     * Get the inflector instance.
597
     */
598
    public static function inflector() : Inflector
599
    {
600
        static $inflector;
601 1
602
        if ($inflector === null) {
603 1
            $inflector = new Inflector(
604
                new CachedWordInflector(new RulesetInflector(
605 1
                    Rules::getSingularRuleset()
606 1
                )),
607
                new CachedWordInflector(new RulesetInflector(
608
                    Rules::getPluralRuleset()
609 1
                ))
610
            );
611
        }
612
613
        return $inflector;
614
    }
615
616
    /**
617
     * Determine if the given value is uncountable.
618
     *
619
     *
620
     */
621
    protected static function uncountable(string $value) : bool
622
    {
623 1
        return \in_array(strtolower($value), static::$uncountable, true);
624
    }
625 1
626
    /**
627
     * Attempt to match the case on two strings.
628
     *
629
     *
630
     */
631
    protected static function matchCase(string $value, string $comparison) : string
632
    {
633
        $functions = ['mb_strtolower', 'mb_strtoupper', 'ucfirst', 'ucwords'];
634
635 1
        foreach ($functions as $function) {
636
            if ($function($comparison) === $comparison) {
637 1
                return $function($value);
638
            }
639
        }
640
641
        return $value;
642
    }
643
}
644