Completed
Push — master ( 157236...851285 )
by Antonio Carlos
03:36
created

Str::pluralStudly()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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