Completed
Pull Request — master (#6)
by Ruud
03:06
created

Str::orderedUuid()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 19
ccs 4
cts 4
cp 1
crap 2
rs 9.6333
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
11
class Str
12
{
13
    use Macroable;
14
15
    /**
16
     * The cache of snake-cased words.
17
     *
18
     * @var array
19
     */
20
    protected static $snakeCache = [];
21
22
    /**
23
     * The cache of camel-cased words.
24
     *
25
     * @var array
26
     */
27
    protected static $camelCache = [];
28
29
    /**
30
     * The cache of studly-cased words.
31
     *
32
     * @var array
33
     */
34
    protected static $studlyCache = [];
35
36
    /**
37
     * The callback that should be used to generate UUIDs.
38
     *
39
     * @var callable
40
     */
41
    protected static $uuidFactory;
42
43
    /**
44
     * Return the remainder of a string after a given value.
45
     *
46
     * @param  string  $subject
47
     * @param  string  $search
48
     * @return string
49
     */
50 1
    public static function after($subject, $search)
51
    {
52 1
        return $search === '' ? $subject : array_reverse(explode($search, $subject, 2))[0];
53
    }
54
55
    /**
56
     * Transliterate a UTF-8 value to ASCII.
57
     *
58
     * @param  string  $value
59
     * @param  string  $language
60
     * @return string
61
     */
62 1
    public static function ascii($value, $language = 'en')
63
    {
64 1
        $languageSpecific = static::languageSpecificCharsArray($language);
65
66
        if (! is_null($languageSpecific)) {
67
            $value = str_replace($languageSpecific[0], $languageSpecific[1], $value);
68
        }
69
70
        foreach (static::charsArray() as $key => $val) {
71
            $value = str_replace($val, $key, $value);
72
        }
73
74 3
        return preg_replace('/[^\x20-\x7E]/u', '', $value);
75
    }
76 3
77
    /**
78 3
     * Get the portion of a string before a given value.
79 1
     *
80
     * @param  string  $subject
81
     * @param  string  $search
82 3
     * @return string
83 3
     */
84
    public static function before($subject, $search)
85
    {
86 3
        return $search === '' ? $subject : explode($search, $subject)[0];
87
    }
88
89
    /**
90
     * Convert a value to camel case.
91
     *
92
     * @param  string  $value
93
     * @return string
94
     */
95
    public static function camel($value)
96 1
    {
97
        if (isset(static::$camelCache[$value])) {
98 1
            return static::$camelCache[$value];
99
        }
100
101
        return static::$camelCache[$value] = lcfirst(static::studly($value));
102
    }
103
104
    /**
105
     * Determine if a given string contains a given substring.
106
     *
107
     * @param  string  $haystack
108 1
     * @param  string|array  $needles
109
     * @return bool
110 1
     */
111 1
    public static function contains($haystack, $needles)
112
    {
113
        foreach ((array) $needles as $needle) {
114 1
            if ($needle !== '' && mb_strpos($haystack, $needle) !== false) {
115
                return true;
116 1
            }
117 1
        }
118
119
        return false;
120 1
    }
121
122
    /**
123
     * Determine if a given string contains all array values.
124
     *
125
     * @param  string  $haystack
126
     * @param  array  $needles
127
     * @return bool
128
     */
129 1
    public static function containsAll($haystack, array $needles)
130
    {
131 1
        foreach ($needles as $needle) {
132 1
            if (! static::contains($haystack, $needle)) {
133
                return false;
134
            }
135 1
        }
136
137
        return true;
138
    }
139
140
    /**
141
     * Determine if a given string ends with a given substring.
142
     *
143
     * @param  string  $haystack
144
     * @param  string|array  $needles
145 3
     * @return bool
146
     */
147 3 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...
148 3
    {
149 3
        foreach ((array) $needles as $needle) {
150
            if (substr($haystack, -strlen($needle)) === (string) $needle) {
151
                return true;
152
            }
153 3
        }
154
155
        return false;
156
    }
157
158
    /**
159
     * Cap a string with a single instance of a given value.
160
     *
161
     * @param  string  $value
162
     * @param  string  $cap
163 1
     * @return string
164
     */
165 1
    public static function finish($value, $cap)
166 1
    {
167 1
        $quoted = preg_quote($cap, '/');
168
169
        return preg_replace('/(?:'.$quoted.')+$/u', '', $value).$cap;
170
    }
171 1
172
    /**
173
     * Determine if a given string matches a given pattern.
174
     *
175
     * @param  string|array  $pattern
176
     * @param  string  $value
177
     * @return bool
178
     */
179
    public static function is($pattern, $value)
180
    {
181 1
        $patterns = Arr::wrap($pattern);
182
183 1
        if (empty($patterns)) {
184 1
            return false;
185 1
        }
186
187
        foreach ($patterns as $pattern) {
188
            // If the given value is an exact match we can of course return true right
189 1
            // from the beginning. Otherwise, we will translate asterisks and do an
190
            // actual pattern match against the two strings to see if they match.
191
            if ($pattern == $value) {
192
                return true;
193
            }
194
195
            $pattern = preg_quote($pattern, '#');
196
197
            // Asterisks are translated into zero-or-more regular expression wildcards
198
            // to make it convenient to check if the strings starts with the given
199 1
            // pattern such as "library/*", making any string check convenient.
200
            $pattern = str_replace('\*', '.*', $pattern);
201 1
202
            if (preg_match('#^'.$pattern.'\z#u', $value) === 1) {
203 1
                return true;
204
            }
205
        }
206
207
        return false;
208
    }
209
210
    /**
211
     * Convert a string to kebab case.
212
     *
213 1
     * @param  string  $value
214
     * @return string
215 1
     */
216
    public static function kebab($value)
217 1
    {
218 1
        return static::snake($value, '-');
219
    }
220
221 1
    /**
222
     * Return the length of the given string.
223
     *
224
     * @param  string  $value
225 1
     * @param  string  $encoding
226 1
     * @return int
227
     */
228
    public static function length($value, $encoding = null)
229 1
    {
230
        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...
231
            return mb_strlen($value, $encoding);
232
        }
233
234 1
        return mb_strlen($value);
235
    }
236 1
237 1
    /**
238
     * Limit the number of characters in a string.
239
     *
240
     * @param  string  $value
241 1
     * @param  int     $limit
242
     * @param  string  $end
243
     * @return string
244
     */
245
    public static function limit($value, $limit = 100, $end = '...')
246
    {
247
        if (mb_strwidth($value, 'UTF-8') <= $limit) {
248
            return $value;
249
        }
250 1
251
        return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')).$end;
252 1
    }
253
254
    /**
255
     * Convert the given string to lower-case.
256
     *
257
     * @param  string  $value
258
     * @return string
259
     */
260
    public static function lower($value)
261
    {
262 3
        return mb_strtolower($value, 'UTF-8');
263
    }
264 3
265 1
    /**
266
     * Limit the number of words in a string.
267
     *
268 3
     * @param  string  $value
269
     * @param  int     $words
270
     * @param  string  $end
271
     * @return string
272
     */
273
    public static function words($value, $words = 100, $end = '...')
274
    {
275
        preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches);
276
277
        if (! isset($matches[0]) || static::length($value) === static::length($matches[0])) {
278
            return $value;
279 1
        }
280
281 1
        return rtrim($matches[0]).$end;
282 1
    }
283
284
    /**
285 1
     * Parse a Class@method style callback into class and method.
286
     *
287
     * @param  string  $callback
288
     * @param  string|null  $default
289
     * @return array
290
     */
291
    public static function parseCallback($callback, $default = null)
292
    {
293
        return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
294 4
    }
295
296 4
    /**
297
     * Get the plural form of an English word.
298
     *
299
     * @param  string  $value
300
     * @param  int     $count
301
     * @return string
302
     */
303
    public static function plural($value, $count = 2)
304
    {
305
        return Pluralizer::plural($value, $count);
306
    }
307 3
308
    /**
309 3
     * Pluralize the last word of an English, studly caps case string.
310
     *
311 3
     * @param  string  $value
312 3
     * @param  int     $count
313
     * @return string
314
     */
315 2
    public static function pluralStudly($value, $count = 2)
316
    {
317
        $parts = preg_split('/(.)(?=[A-Z])/u', $value, -1, PREG_SPLIT_DELIM_CAPTURE);
318
319
        $lastWord = array_pop($parts);
320
321
        return implode('', $parts).self::plural($lastWord, $count);
322
    }
323
324
    /**
325 1
     * Generate a more truly "random" alpha-numeric string.
326
     *
327 1
     * @param  int  $length
328
     * @return string
329
     */
330
    public static function random($length = 16)
331
    {
332
        $string = '';
333
334
        while (($len = strlen($string)) < $length) {
335
            $size = $length - $len;
336
337
            $bytes = random_bytes($size);
338
339
            $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
340
        }
341
342
        return $string;
343
    }
344
345
    /**
346
     * Replace a given value in the string sequentially with an array.
347
     *
348
     * @param  string  $search
349
     * @param  array   $replace
350
     * @param  string  $subject
351
     * @return string
352
     */
353
    public static function replaceArray($search, array $replace, $subject)
354
    {
355
        $segments = explode($search, $subject);
356
357
        $result = array_shift($segments);
358
359
        foreach ($segments as $segment) {
360
            $result .= (array_shift($replace) ?? $search).$segment;
361
        }
362
363
        return $result;
364 1
    }
365
366 1
    /**
367
     * Replace the first occurrence of a given value in the string.
368 1
     *
369 1
     * @param  string  $search
370
     * @param  string  $replace
371 1
     * @param  string  $subject
372
     * @return string
373 1
     */
374 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...
375
    {
376 1
        if ($search == '') {
377
            return $subject;
378
        }
379
380
        $position = strpos($subject, $search);
381
382
        if ($position !== false) {
383
            return substr_replace($subject, $replace, $position, strlen($search));
384
        }
385
386
        return $subject;
387 1
    }
388
389 1
    /**
390
     * Replace the last occurrence of a given value in the string.
391 1
     *
392
     * @param  string  $search
393 1
     * @param  string  $replace
394 1
     * @param  string  $subject
395
     * @return string
396
     */
397 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...
398
    {
399
        $position = strrpos($subject, $search);
400
401
        if ($position !== false) {
402
            return substr_replace($subject, $replace, $position, strlen($search));
403
        }
404
405
        return $subject;
406
    }
407
408 1
    /**
409
     * Begin a string with a single instance of a given value.
410 1
     *
411 1
     * @param  string  $value
412
     * @param  string  $prefix
413
     * @return string
414 1
     */
415
    public static function start($value, $prefix)
416 1
    {
417 1
        $quoted = preg_quote($prefix, '/');
418
419
        return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value);
420 1
    }
421
422
    /**
423
     * Convert the given string to upper-case.
424
     *
425
     * @param  string  $value
426
     * @return string
427
     */
428
    public static function upper($value)
429
    {
430
        return mb_strtoupper($value, 'UTF-8');
431 1
    }
432
433 1
    /**
434
     * Convert the given string to title case.
435 1
     *
436 1
     * @param  string  $value
437
     * @return string
438
     */
439 1
    public static function title($value)
440
    {
441
        return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
442
    }
443
444
    /**
445
     * Get the singular form of an English word.
446
     *
447
     * @param  string  $value
448
     * @return string
449 1
     */
450
    public static function singular($value)
451 1
    {
452
        return Pluralizer::singular($value);
453 1
    }
454
455
    /**
456
     * Generate a URL friendly "slug" from a given string.
457
     *
458
     * @param  string  $title
459
     * @param  string  $separator
460
     * @param  string|null  $language
461
     * @return string
462 2
     */
463
    public static function slug($title, $separator = '-', $language = 'en')
464 2
    {
465
        $title = $language ? static::ascii($title, $language) : $title;
466
467
        // Convert all dashes/underscores into separator
468
        $flip = $separator === '-' ? '_' : '-';
469
470
        $title = preg_replace('!['.preg_quote($flip).']+!u', $separator, $title);
471
472
        // Replace @ with the word 'at'
473 1
        $title = str_replace('@', $separator.'at'.$separator, $title);
474
475 1
        // Remove all characters that are not the separator, letters, numbers, or whitespace.
476
        $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', static::lower($title));
477
478
        // Replace all separator characters and whitespace by a single separator
479
        $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);
480
481
        return trim($title, $separator);
482
    }
483
484
    /**
485
     * Convert a string to snake case.
486
     *
487
     * @param  string  $value
488
     * @param  string  $delimiter
489
     * @return string
490
     */
491
    public static function snake($value, $delimiter = '_')
492
    {
493
        $key = $value;
494
495
        if (isset(static::$snakeCache[$key][$delimiter])) {
496
            return static::$snakeCache[$key][$delimiter];
497 1
        }
498
499 1
        if (! ctype_lower($value)) {
500
            $value = preg_replace('/\s+/u', '', ucwords($value));
501
502 1
            $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
503
        }
504 1
505
        return static::$snakeCache[$key][$delimiter] = $value;
506
    }
507 1
508
    /**
509
     * Determine if a given string starts with a given substring.
510 1
     *
511
     * @param  string  $haystack
512
     * @param  string|array  $needles
513 1
     * @return bool
514
     */
515 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...
516
    {
517
        foreach ((array) $needles as $needle) {
518
            if ($needle !== '' && substr($haystack, 0, strlen($needle)) === (string) $needle) {
519
                return true;
520
            }
521
        }
522
523
        return false;
524
    }
525 2
526
    /**
527 2
     * Convert a value to studly caps case.
528
     *
529 2
     * @param  string  $value
530
     * @return string
531
     */
532
    public static function studly($value)
533 2
    {
534 2
        $key = $value;
535
536 2
        if (isset(static::$studlyCache[$key])) {
537
            return static::$studlyCache[$key];
538
        }
539 2
540
        $value = ucwords(str_replace(['-', '_'], ' ', $value));
541
542
        return static::$studlyCache[$key] = str_replace(' ', '', $value);
543
    }
544
545
    /**
546
     * Returns the portion of string specified by the start and length parameters.
547
     *
548
     * @param  string  $string
549 1
     * @param  int  $start
550
     * @param  int|null  $length
551 1
     * @return string
552 1
     */
553 1
    public static function substr($string, $start, $length = null)
554
    {
555
        return mb_substr($string, $start, $length, 'UTF-8');
556
    }
557 1
558
    /**
559
     * Make a string's first character uppercase.
560
     *
561
     * @param  string  $string
562
     * @return string
563
     */
564
    public static function ucfirst($string)
565
    {
566 2
        return static::upper(static::substr($string, 0, 1)).static::substr($string, 1);
567
    }
568 2
569
    /**
570 2
     * Generate a UUID (version 4).
571 2
     *
572
     * @return \Ramsey\Uuid\UuidInterface
573
     */
574 2
    public static function uuid()
575
    {
576 2
        return static::$uuidFactory
577
                    ? call_user_func(static::$uuidFactory)
578
                    : Uuid::uuid4();
579
    }
580
581
    /**
582
     * Generate a time-ordered UUID (version 4).
583
     *
584
     * @return \Ramsey\Uuid\UuidInterface
585
     */
586
    public static function orderedUuid()
587 3
    {
588
        if (static::$uuidFactory) {
589 3
            return call_user_func(static::$uuidFactory);
590
        }
591
592
        $factory = new UuidFactory();
593
594
        $factory->setRandomGenerator(new CombGenerator(
595
            $factory->getRandomGenerator(),
596
            $factory->getNumberConverter()
597
        ));
598 1
599
        $factory->setCodec(new TimestampFirstCombCodec(
600 1
            $factory->getUuidBuilder()
601
        ));
602
603
        return $factory->uuid4();
604
    }
605
606
    /**
607
     * Set the callable that will be used to generate UUIDs.
608 1
     *
609
     * @param  callable
610 1
     * @return void
611
     */
612 1
    public static function createUuidsUsing(callable $factory = null)
613
    {
614
        static::$uuidFactory = $factory;
615
    }
616
617
    /**
618
     * Indicate that UUIDs should be created normally and not using a custom factory.
619
     *
620 1
     * @return void
621
     */
622 1
    public static function createUuidsNormally()
623
    {
624
        static::$uuidFactory = null;
625
    }
626 1
627
    /**
628 1
     * Returns the replacements for the ascii method.
629 1
     *
630 1
     * Note: Adapted from Stringy\Stringy.
631
     *
632
     * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
633 1
     *
634 1
     * @return array
635
     */
636
    protected static function charsArray()
637 1
    {
638
        static $charsArray;
639
640
        if (isset($charsArray)) {
641
            return $charsArray;
642
        }
643
644
        return $charsArray = [
645
            '0'    => ['°', '₀', '۰', '0'],
646
            '1'    => ['¹', '₁', '۱', '1'],
647
            '2'    => ['²', '₂', '۲', '2'],
648
            '3'    => ['³', '₃', '۳', '3'],
649
            '4'    => ['⁴', '₄', '۴', '٤', '4'],
650
            '5'    => ['⁵', '₅', '۵', '٥', '5'],
651
            '6'    => ['⁶', '₆', '۶', '٦', '6'],
652
            '7'    => ['⁷', '₇', '۷', '7'],
653
            '8'    => ['⁸', '₈', '۸', '8'],
654
            '9'    => ['⁹', '₉', '۹', '9'],
655
            'a'    => ['à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ', 'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ā', 'ą', 'å', 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ', 'ἇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ', 'ά', 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', 'အ', 'ာ', 'ါ', 'ǻ', 'ǎ', 'ª', 'ა', 'अ', 'ا', 'a', 'ä', 'א'],
656
            'b'    => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b', 'ב'],
657
            'c'    => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'],
658
            'd'    => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd', 'ד'],
659
            'e'    => ['é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ', 'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ', 'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э', 'є', 'ə', 'ဧ', 'ေ', 'ဲ', 'ე', 'ए', 'إ', 'ئ', 'e'],
660
            'f'    => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f', 'פ', 'ף'],
661
            'g'    => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', 'g', 'ג'],
662
            'h'    => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h', 'ה'],
663
            'i'    => ['í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į', 'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ', 'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'і', 'ї', 'и', 'ဣ', 'ိ', 'ီ', 'ည်', 'ǐ', 'ი', 'इ', 'ی', 'i', 'י'],
664
            'j'    => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'],
665
            'k'    => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', 'ک', 'k', 'ק'],
666
            'l'    => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', 'l', 'ל'],
667
            'm'    => ['м', 'μ', 'م', 'မ', 'მ', 'm', 'מ', 'ם'],
668
            'n'    => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ', 'n', 'נ'],
669
            'o'    => ['ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ', 'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő', 'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό', 'о', 'و', 'ို', 'ǒ', 'ǿ', 'º', 'ო', 'ओ', 'o', 'ö'],
670 3
            'p'    => ['п', 'π', 'ပ', 'პ', 'پ', 'p', 'פ', 'ף'],
671
            'q'    => ['ყ', 'q'],
672 3
            'r'    => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r', 'ר'],
673
            's'    => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს', 's', 'ס'],
674 3
            't'    => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ', 't', 'ת'],
675 3
            'u'    => ['ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ', 'ự', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', 'ဉ', 'ု', 'ူ', 'ǔ', 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'უ', 'उ', 'u', 'ў', 'ü'],
676
            'v'    => ['в', 'ვ', 'ϐ', 'v', 'ו'],
677
            'w'    => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'],
678
            'x'    => ['χ', 'ξ', 'x'],
679 1
            'y'    => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'],
680
            'z'    => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z', 'ז'],
681
            'aa'   => ['ع', 'आ', 'آ'],
682
            'ae'   => ['æ', 'ǽ'],
683
            'ai'   => ['ऐ'],
684
            'ch'   => ['ч', 'ჩ', 'ჭ', 'چ'],
685
            'dj'   => ['ђ', 'đ'],
686
            'dz'   => ['џ', 'ძ', 'דז'],
687
            'ei'   => ['ऍ'],
688
            'gh'   => ['غ', 'ღ'],
689
            'ii'   => ['ई'],
690
            'ij'   => ['ij'],
691
            'kh'   => ['х', 'خ', 'ხ'],
692
            'lj'   => ['љ'],
693
            'nj'   => ['њ'],
694
            'oe'   => ['ö', 'œ', 'ؤ'],
695
            'oi'   => ['ऑ'],
696
            'oii'  => ['ऒ'],
697
            'ps'   => ['ψ'],
698
            'sh'   => ['ш', 'შ', 'ش', 'ש'],
699
            'shch' => ['щ'],
700
            'ss'   => ['ß'],
701
            'sx'   => ['ŝ'],
702
            'th'   => ['þ', 'ϑ', 'θ', 'ث', 'ذ', 'ظ'],
703
            'ts'   => ['ц', 'ც', 'წ'],
704
            'ue'   => ['ü'],
705
            'uu'   => ['ऊ'],
706
            'ya'   => ['я'],
707
            'yu'   => ['ю'],
708
            'zh'   => ['ж', 'ჟ', 'ژ'],
709
            '(c)'  => ['©'],
710
            'A'    => ['Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ', 'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Å', 'Ā', 'Ą', 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ', 'Ἇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ', 'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 'А', 'Ǻ', 'Ǎ', 'A', 'Ä'],
711
            'B'    => ['Б', 'Β', 'ब', 'B'],
712
            'C'    => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ', 'C'],
713
            'D'    => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', 'D'],
714
            'E'    => ['É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ', 'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ', 'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э', 'Є', 'Ə', 'E'],
715
            'F'    => ['Ф', 'Φ', 'F'],
716
            'G'    => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'],
717
            'H'    => ['Η', 'Ή', 'Ħ', 'H'],
718
            'I'    => ['Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į', 'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ', 'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', 'Ǐ', 'ϒ', 'I'],
719
            'J'    => ['J'],
720
            'K'    => ['К', 'Κ', 'K'],
721
            'L'    => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'],
722
            'M'    => ['М', 'Μ', 'M'],
723
            'N'    => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'],
724
            'O'    => ['Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ', 'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ø', 'Ō', 'Ő', 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ', 'Ὸ', 'Ό', 'О', 'Ө', 'Ǒ', 'Ǿ', 'O', 'Ö'],
725
            'P'    => ['П', 'Π', 'P'],
726
            'Q'    => ['Q'],
727
            'R'    => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'],
728
            'S'    => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'],
729
            'T'    => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'],
730
            'U'    => ['Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ', 'Ự', 'Û', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', 'Ǔ', 'Ǖ', 'Ǘ', 'Ǚ', 'Ǜ', 'U', 'Ў', 'Ü'],
731
            'V'    => ['В', 'V'],
732
            'W'    => ['Ω', 'Ώ', 'Ŵ', 'W'],
733
            'X'    => ['Χ', 'Ξ', 'X'],
734
            'Y'    => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'],
735
            'Z'    => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'],
736
            'AE'   => ['Æ', 'Ǽ'],
737
            'Ch'   => ['Ч'],
738
            'Dj'   => ['Ђ'],
739
            'Dz'   => ['Џ'],
740
            'Gx'   => ['Ĝ'],
741
            'Hx'   => ['Ĥ'],
742
            'Ij'   => ['IJ'],
743
            'Jx'   => ['Ĵ'],
744
            'Kh'   => ['Х'],
745
            'Lj'   => ['Љ'],
746
            'Nj'   => ['Њ'],
747
            'Oe'   => ['Œ'],
748
            'Ps'   => ['Ψ'],
749
            'Sh'   => ['Ш', 'ש'],
750
            'Shch' => ['Щ'],
751
            'Ss'   => ['ẞ'],
752
            'Th'   => ['Þ', 'Θ', 'ת'],
753
            'Ts'   => ['Ц'],
754
            'Ya'   => ['Я', 'יא'],
755
            'Yu'   => ['Ю', 'יו'],
756
            'Zh'   => ['Ж'],
757
            ' '    => ["\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80", "\xEF\xBE\xA0"],
758
        ];
759
    }
760
761
    /**
762
     * Returns the language specific replacements for the ascii method.
763
     *
764
     * Note: Adapted from Stringy\Stringy.
765
     *
766
     * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
767
     *
768
     * @param  string  $language
769
     * @return array|null
770
     */
771
    protected static function languageSpecificCharsArray($language)
772
    {
773
        static $languageSpecific;
774
775
        if (! isset($languageSpecific)) {
776
            $languageSpecific = [
777
                'bg' => [
778
                    ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'],
779
                    ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'],
780
                ],
781
                'da' => [
782
                    ['æ', 'ø', 'å', 'Æ', 'Ø', 'Å'],
783
                    ['ae', 'oe', 'aa', 'Ae', 'Oe', 'Aa'],
784
                ],
785
                'de' => [
786
                    ['ä',  'ö',  'ü',  'Ä',  'Ö',  'Ü'],
787
                    ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'],
788
                ],
789
                'he' => [
790
                    ['א', 'ב', 'ג', 'ד', 'ה', 'ו'],
791
                    ['ז', 'ח', 'ט', 'י', 'כ', 'ל'],
792
                    ['מ', 'נ', 'ס', 'ע', 'פ', 'צ'],
793
                    ['ק', 'ר', 'ש', 'ת', 'ן', 'ץ', 'ך', 'ם', 'ף'],
794
                ],
795
                'ro' => [
796
                    ['ă', 'â', 'î', 'ș', 'ț', 'Ă', 'Â', 'Î', 'Ș', 'Ț'],
797
                    ['a', 'a', 'i', 's', 't', 'A', 'A', 'I', 'S', 'T'],
798
                ],
799
            ];
800
        }
801
802
        return $languageSpecific[$language] ?? null;
803
    }
804
}
805