Completed
Push — master ( 8eb5fd...97a003 )
by Antonio Carlos
03:41
created

Str::isAscii()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace IlluminateAgnostic\Collection\Support;
4
5
use IlluminateAgnostic\Collection\Support\Traits\Macroable;
6
use Ramsey\Uuid\Codec\TimestampFirstCombCodec;
7
use Ramsey\Uuid\Generator\CombGenerator;
8
use Ramsey\Uuid\Uuid;
9
use Ramsey\Uuid\UuidFactory;
10
use voku\helper\ASCII;
11
12
class Str
13
{
14
    use Macroable;
15
16
    /**
17
     * The cache of snake-cased words.
18
     *
19
     * @var array
20
     */
21
    protected static $snakeCache = [];
22
23
    /**
24
     * The cache of camel-cased words.
25
     *
26
     * @var array
27
     */
28
    protected static $camelCache = [];
29
30
    /**
31
     * The cache of studly-cased words.
32
     *
33
     * @var array
34
     */
35
    protected static $studlyCache = [];
36
37
    /**
38
     * The callback that should be used to generate UUIDs.
39
     *
40
     * @var callable
41
     */
42
    protected static $uuidFactory;
43
44
    /**
45
     * Get a new stringable object from the given string.
46
     *
47
     * @param  string  $string
48
     * @return \IlluminateAgnostic\Collection\Support\Stringable
49
     */
50
    public static function of($string)
51
    {
52
        return new Stringable($string);
53
    }
54
55
    /**
56
     * Return the remainder of a string after the first occurrence of a given value.
57
     *
58
     * @param  string  $subject
59
     * @param  string  $search
60
     * @return string
61
     */
62 2
    public static function after($subject, $search)
63
    {
64 2
        return $search === '' ? $subject : array_reverse(explode($search, $subject, 2))[0];
65
    }
66
67
    /**
68
     * Return the remainder of a string after the last occurrence of a given value.
69
     *
70
     * @param  string  $subject
71
     * @param  string  $search
72
     * @return string
73
     */
74 1
    public static function afterLast($subject, $search)
75
    {
76 1
        if ($search === '') {
77 1
            return $subject;
78
        }
79
80 1
        $position = strrpos($subject, (string) $search);
81
82 1
        if ($position === false) {
83 1
            return $subject;
84
        }
85
86 1
        return substr($subject, $position + strlen($search));
87
    }
88
89
    /**
90
     * Transliterate a UTF-8 value to ASCII.
91
     *
92
     * @param  string  $value
93
     * @param  string  $language
94
     * @return string
95
     */
96 4
    public static function ascii($value, $language = 'en')
97
    {
98 4
        return ASCII::to_ascii((string) $value, $language);
99
    }
100
101
    /**
102
     * Get the portion of a string before the first occurrence of a given value.
103
     *
104
     * @param  string  $subject
105
     * @param  string  $search
106
     * @return string
107
     */
108 1
    public static function before($subject, $search)
109
    {
110 1
        return $search === '' ? $subject : explode($search, $subject)[0];
111
    }
112
113
    /**
114
     * Get the portion of a string before the last occurrence of a given value.
115
     *
116
     * @param  string  $subject
117
     * @param  string  $search
118
     * @return string
119
     */
120 2
    public static function beforeLast($subject, $search)
121
    {
122 2
        if ($search === '') {
123 1
            return $subject;
124
        }
125
126 2
        $pos = mb_strrpos($subject, $search);
127
128 2
        if ($pos === false) {
129 1
            return $subject;
130
        }
131
132 2
        return static::substr($subject, 0, $pos);
133
    }
134
135
    /**
136
     * Get the portion of a string between two given values.
137
     *
138
     * @param  string  $subject
139
     * @param  string  $from
140
     * @param  string  $to
141
     * @return string
142
     */
143 1
    public static function between($subject, $from, $to)
144
    {
145 1
        if ($from === '' || $to === '') {
146 1
            return $subject;
147
        }
148
149 1
        return static::beforeLast(static::after($subject, $from), $to);
150
    }
151
152
    /**
153
     * Convert a value to camel case.
154
     *
155
     * @param  string  $value
156
     * @return string
157
     */
158 1
    public static function camel($value)
159
    {
160 1
        if (isset(static::$camelCache[$value])) {
161 1
            return static::$camelCache[$value];
162
        }
163
164 1
        return static::$camelCache[$value] = lcfirst(static::studly($value));
165
    }
166
167
    /**
168
     * Determine if a given string contains a given substring.
169
     *
170
     * @param  string  $haystack
171
     * @param  string|string[]  $needles
172
     * @return bool
173
     */
174 3
    public static function contains($haystack, $needles)
175
    {
176 3
        foreach ((array) $needles as $needle) {
177 3
            if ($needle !== '' && mb_strpos($haystack, $needle) !== false) {
178 3
                return true;
179
            }
180
        }
181
182 3
        return false;
183
    }
184
185
    /**
186
     * Determine if a given string contains all array values.
187
     *
188
     * @param  string  $haystack
189
     * @param  string[]  $needles
190
     * @return bool
191
     */
192 1
    public static function containsAll($haystack, array $needles)
193
    {
194 1
        foreach ($needles as $needle) {
195 1
            if (! static::contains($haystack, $needle)) {
196 1
                return false;
197
            }
198
        }
199
200 1
        return true;
201
    }
202
203
    /**
204
     * Determine if a given string ends with a given substring.
205
     *
206
     * @param  string  $haystack
207
     * @param  string|string[]  $needles
208
     * @return bool
209
     */
210 1 View Code Duplication
    public static function endsWith($haystack, $needles)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
211
    {
212 1
        foreach ((array) $needles as $needle) {
213 1
            if (substr($haystack, -strlen($needle)) === (string) $needle) {
214 1
                return true;
215
            }
216
        }
217
218 1
        return false;
219
    }
220
221
    /**
222
     * Cap a string with a single instance of a given value.
223
     *
224
     * @param  string  $value
225
     * @param  string  $cap
226
     * @return string
227
     */
228 1
    public static function finish($value, $cap)
229
    {
230 1
        $quoted = preg_quote($cap, '/');
231
232 1
        return preg_replace('/(?:'.$quoted.')+$/u', '', $value).$cap;
233
    }
234
235
    /**
236
     * Determine if a given string matches a given pattern.
237
     *
238
     * @param  string|array  $pattern
239
     * @param  string  $value
240
     * @return bool
241
     */
242 1
    public static function is($pattern, $value)
243
    {
244 1
        $patterns = Arr::wrap($pattern);
245
246 1
        if (empty($patterns)) {
247 1
            return false;
248
        }
249
250 1
        foreach ($patterns as $pattern) {
251
            // If the given value is an exact match we can of course return true right
252
            // from the beginning. Otherwise, we will translate asterisks and do an
253
            // actual pattern match against the two strings to see if they match.
254 1
            if ($pattern == $value) {
255 1
                return true;
256
            }
257
258 1
            $pattern = preg_quote($pattern, '#');
259
260
            // Asterisks are translated into zero-or-more regular expression wildcards
261
            // to make it convenient to check if the strings starts with the given
262
            // pattern such as "library/*", making any string check convenient.
263 1
            $pattern = str_replace('\*', '.*', $pattern);
264
265 1
            if (preg_match('#^'.$pattern.'\z#u', $value) === 1) {
266 1
                return true;
267
            }
268
        }
269
270 1
        return false;
271
    }
272
273
    /**
274
     * Determine if a given string is 7 bit ASCII.
275
     *
276
     * @param  string  $value
277
     * @return bool
278
     */
279 1
    public static function isAscii($value)
280
    {
281 1
        return ASCII::is_ascii((string) $value);
282
    }
283
284
    /**
285
     * Determine if a given string is a valid UUID.
286
     *
287
     * @param  string  $value
288
     * @return bool
289
     */
290 20
    public static function isUuid($value)
291
    {
292 20
        if (! is_string($value)) {
293
            return false;
294
        }
295
296 20
        return preg_match('/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iD', $value) > 0;
297
    }
298
299
    /**
300
     * Convert a string to kebab case.
301
     *
302
     * @param  string  $value
303
     * @return string
304
     */
305 1
    public static function kebab($value)
306
    {
307 1
        return static::snake($value, '-');
308
    }
309
310
    /**
311
     * Return the length of the given string.
312
     *
313
     * @param  string  $value
314
     * @param  string|null  $encoding
315
     * @return int
316
     */
317 3
    public static function length($value, $encoding = null)
318
    {
319 3
        if ($encoding) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $encoding of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
320 1
            return mb_strlen($value, $encoding);
321
        }
322
323 3
        return mb_strlen($value);
324
    }
325
326
    /**
327
     * Limit the number of characters in a string.
328
     *
329
     * @param  string  $value
330
     * @param  int  $limit
331
     * @param  string  $end
332
     * @return string
333
     */
334 1
    public static function limit($value, $limit = 100, $end = '...')
335
    {
336 1
        if (mb_strwidth($value, 'UTF-8') <= $limit) {
337 1
            return $value;
338
        }
339
340 1
        return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')).$end;
341
    }
342
343
    /**
344
     * Convert the given string to lower-case.
345
     *
346
     * @param  string  $value
347
     * @return string
348
     */
349 5
    public static function lower($value)
350
    {
351 5
        return mb_strtolower($value, 'UTF-8');
352
    }
353
354
    /**
355
     * Limit the number of words in a string.
356
     *
357
     * @param  string  $value
358
     * @param  int  $words
359
     * @param  string  $end
360
     * @return string
361
     */
362 3
    public static function words($value, $words = 100, $end = '...')
363
    {
364 3
        preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches);
365
366 3
        if (! isset($matches[0]) || static::length($value) === static::length($matches[0])) {
367 3
            return $value;
368
        }
369
370 2
        return rtrim($matches[0]).$end;
371
    }
372
373
    /**
374
     * Parse a Class[@]method style callback into class and method.
375
     *
376
     * @param  string  $callback
377
     * @param  string|null  $default
378
     * @return array<int, string|null>
0 ignored issues
show
Documentation introduced by
The doc-type array<int, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
379
     */
380 1
    public static function parseCallback($callback, $default = null)
381
    {
382 1
        return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
383
    }
384
385
    /**
386
     * Get the plural form of an English word.
387
     *
388
     * @param  string  $value
389
     * @param  int  $count
390
     * @return string
391
     */
392
    public static function plural($value, $count = 2)
393
    {
394
        return Pluralizer::plural($value, $count);
395
    }
396
397
    /**
398
     * Pluralize the last word of an English, studly caps case string.
399
     *
400
     * @param  string  $value
401
     * @param  int  $count
402
     * @return string
403
     */
404
    public static function pluralStudly($value, $count = 2)
405
    {
406
        $parts = preg_split('/(.)(?=[A-Z])/u', $value, -1, PREG_SPLIT_DELIM_CAPTURE);
407
408
        $lastWord = array_pop($parts);
409
410
        return implode('', $parts).self::plural($lastWord, $count);
411
    }
412
413
    /**
414
     * Generate a more truly "random" alpha-numeric string.
415
     *
416
     * @param  int  $length
417
     * @return string
418
     */
419 1
    public static function random($length = 16)
420
    {
421 1
        $string = '';
422
423 1
        while (($len = strlen($string)) < $length) {
424 1
            $size = $length - $len;
425
426 1
            $bytes = random_bytes($size);
427
428 1
            $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
429
        }
430
431 1
        return $string;
432
    }
433
434
    /**
435
     * Replace a given value in the string sequentially with an array.
436
     *
437
     * @param  string  $search
438
     * @param  array<int|string, string>  $replace
439
     * @param  string  $subject
440
     * @return string
441
     */
442 1
    public static function replaceArray($search, array $replace, $subject)
443
    {
444 1
        $segments = explode($search, $subject);
445
446 1
        $result = array_shift($segments);
447
448 1
        foreach ($segments as $segment) {
449 1
            $result .= (array_shift($replace) ?? $search).$segment;
450
        }
451
452 1
        return $result;
453
    }
454
455
    /**
456
     * Replace the first occurrence of a given value in the string.
457
     *
458
     * @param  string  $search
459
     * @param  string  $replace
460
     * @param  string  $subject
461
     * @return string
462
     */
463 1 View Code Duplication
    public static function replaceFirst($search, $replace, $subject)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
464
    {
465 1
        if ($search == '') {
466 1
            return $subject;
467
        }
468
469 1
        $position = strpos($subject, $search);
470
471 1
        if ($position !== false) {
472 1
            return substr_replace($subject, $replace, $position, strlen($search));
473
        }
474
475 1
        return $subject;
476
    }
477
478
    /**
479
     * Replace the last occurrence of a given value in the string.
480
     *
481
     * @param  string  $search
482
     * @param  string  $replace
483
     * @param  string  $subject
484
     * @return string
485
     */
486 1 View Code Duplication
    public static function replaceLast($search, $replace, $subject)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
487
    {
488 1
        $position = strrpos($subject, $search);
489
490 1
        if ($position !== false) {
491 1
            return substr_replace($subject, $replace, $position, strlen($search));
492
        }
493
494 1
        return $subject;
495
    }
496
497
    /**
498
     * Begin a string with a single instance of a given value.
499
     *
500
     * @param  string  $value
501
     * @param  string  $prefix
502
     * @return string
503
     */
504 1
    public static function start($value, $prefix)
505
    {
506 1
        $quoted = preg_quote($prefix, '/');
507
508 1
        return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value);
509
    }
510
511
    /**
512
     * Convert the given string to upper-case.
513
     *
514
     * @param  string  $value
515
     * @return string
516
     */
517 2
    public static function upper($value)
518
    {
519 2
        return mb_strtoupper($value, 'UTF-8');
520
    }
521
522
    /**
523
     * Convert the given string to title case.
524
     *
525
     * @param  string  $value
526
     * @return string
527
     */
528 1
    public static function title($value)
529
    {
530 1
        return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
531
    }
532
533
    /**
534
     * Get the singular form of an English word.
535
     *
536
     * @param  string  $value
537
     * @return string
538
     */
539
    public static function singular($value)
540
    {
541
        return Pluralizer::singular($value);
542
    }
543
544
    /**
545
     * Generate a URL friendly "slug" from a given string.
546
     *
547
     * @param  string  $title
548
     * @param  string  $separator
549
     * @param  string|null  $language
550
     * @return string
551
     */
552 2
    public static function slug($title, $separator = '-', $language = 'en')
553
    {
554 2
        $title = $language ? static::ascii($title, $language) : $title;
555
556
        // Convert all dashes/underscores into separator
557 2
        $flip = $separator === '-' ? '_' : '-';
558
559 2
        $title = preg_replace('!['.preg_quote($flip).']+!u', $separator, $title);
560
561
        // Replace @ with the word 'at'
562 2
        $title = str_replace('@', $separator.'at'.$separator, $title);
563
564
        // Remove all characters that are not the separator, letters, numbers, or whitespace.
565 2
        $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', static::lower($title));
566
567
        // Replace all separator characters and whitespace by a single separator
568 2
        $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);
569
570 2
        return trim($title, $separator);
571
    }
572
573
    /**
574
     * Convert a string to snake case.
575
     *
576
     * @param  string  $value
577
     * @param  string  $delimiter
578
     * @return string
579
     */
580 2
    public static function snake($value, $delimiter = '_')
581
    {
582 2
        $key = $value;
583
584 2
        if (isset(static::$snakeCache[$key][$delimiter])) {
585
            return static::$snakeCache[$key][$delimiter];
586
        }
587
588 2
        if (! ctype_lower($value)) {
589 2
            $value = preg_replace('/\s+/u', '', ucwords($value));
590
591 2
            $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
592
        }
593
594 2
        return static::$snakeCache[$key][$delimiter] = $value;
595
    }
596
597
    /**
598
     * Determine if a given string starts with a given substring.
599
     *
600
     * @param  string  $haystack
601
     * @param  string|string[]  $needles
602
     * @return bool
603
     */
604 1 View Code Duplication
    public static function startsWith($haystack, $needles)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
605
    {
606 1
        foreach ((array) $needles as $needle) {
607 1
            if ((string) $needle !== '' && substr($haystack, 0, strlen($needle)) === (string) $needle) {
608 1
                return true;
609
            }
610
        }
611
612 1
        return false;
613
    }
614
615
    /**
616
     * Convert a value to studly caps case.
617
     *
618
     * @param  string  $value
619
     * @return string
620
     */
621 2
    public static function studly($value)
622
    {
623 2
        $key = $value;
624
625 2
        if (isset(static::$studlyCache[$key])) {
626 2
            return static::$studlyCache[$key];
627
        }
628
629 2
        $value = ucwords(str_replace(['-', '_'], ' ', $value));
630
631 2
        return static::$studlyCache[$key] = str_replace(' ', '', $value);
632
    }
633
634
    /**
635
     * Returns the portion of string specified by the start and length parameters.
636
     *
637
     * @param  string  $string
638
     * @param  int  $start
639
     * @param  int|null  $length
640
     * @return string
641
     */
642 4
    public static function substr($string, $start, $length = null)
643
    {
644 4
        return mb_substr($string, $start, $length, 'UTF-8');
645
    }
646
647
    /**
648
     * Make a string's first character uppercase.
649
     *
650
     * @param  string  $string
651
     * @return string
652
     */
653 1
    public static function ucfirst($string)
654
    {
655 1
        return static::upper(static::substr($string, 0, 1)).static::substr($string, 1);
656
    }
657
658
    /**
659
     * Generate a UUID (version 4).
660
     *
661
     * @return \Ramsey\Uuid\UuidInterface
662
     */
663 1
    public static function uuid()
664
    {
665 1
        return static::$uuidFactory
666
                    ? call_user_func(static::$uuidFactory)
667 1
                    : Uuid::uuid4();
668
    }
669
670
    /**
671
     * Generate a time-ordered UUID (version 4).
672
     *
673
     * @return \Ramsey\Uuid\UuidInterface
674
     */
675 1
    public static function orderedUuid()
676
    {
677 1
        if (static::$uuidFactory) {
678
            return call_user_func(static::$uuidFactory);
679
        }
680
681 1
        $factory = new UuidFactory();
682
683 1
        $factory->setRandomGenerator(new CombGenerator(
684 1
            $factory->getRandomGenerator(),
685 1
            $factory->getNumberConverter()
686
        ));
687
688 1
        $factory->setCodec(new TimestampFirstCombCodec(
689 1
            $factory->getUuidBuilder()
690
        ));
691
692 1
        return $factory->uuid4();
693
    }
694
695
    /**
696
     * Set the callable that will be used to generate UUIDs.
697
     *
698
     * @param  callable|null  $factory
699
     * @return void
700
     */
701
    public static function createUuidsUsing(callable $factory = null)
702
    {
703
        static::$uuidFactory = $factory;
704
    }
705
706
    /**
707
     * Indicate that UUIDs should be created normally and not using a custom factory.
708
     *
709
     * @return void
710
     */
711
    public static function createUuidsNormally()
712
    {
713
        static::$uuidFactory = null;
714
    }
715
}
716