Passed
Push — develop ( 834802...eaf19a )
by nguereza
02:28
created

Str::firstLine()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 15
rs 10
1
<?php
2
3
/**
4
 * Platine Stdlib
5
 *
6
 * Platine Stdlib is a the collection of frequently used php features
7
 *
8
 * This content is released under the MIT License (MIT)
9
 *
10
 * Copyright (c) 2020 Platine Stdlib
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a copy
13
 * of this software and associated documentation files (the "Software"), to deal
14
 * in the Software without restriction, including without limitation the rights
15
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
 * copies of the Software, and to permit persons to whom the Software is
17
 * furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included in all
20
 * copies or substantial portions of the Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
 * SOFTWARE.
29
 */
30
31
/**
32
 *  @file Str.php
33
 *
34
 *  The String helper class
35
 *
36
 *  @package    Platine\Stdlib\Helper
37
 *  @author Platine Developers Team
38
 *  @copyright  Copyright (c) 2020
39
 *  @license    http://opensource.org/licenses/MIT  MIT License
40
 *  @link   http://www.iacademy.cf
41
 *  @version 1.0.0
42
 *  @filesource
43
 */
44
45
declare(strict_types=1);
46
47
namespace Platine\Stdlib\Helper;
48
49
use DateTime;
50
use DateTimeInterface;
51
use JsonSerializable;
52
use Throwable;
53
use Traversable;
54
55
/**
56
 * Class Str
57
 * @package Platine\Stdlib\Helper
58
 */
59
class Str
60
{
61
62
    /**
63
     * The cache of snake-cased words.
64
     *
65
     * @var array<string, string>
66
     */
67
    protected static array $snakeCache = [];
68
69
    /**
70
     * The cache of camel-cased words.
71
     *
72
     * @var array<string, string>
73
     */
74
    protected static array $camelCache = [];
75
76
    /**
77
     * The cache of studly-cased words.
78
     *
79
     * @var array<string, string>
80
     */
81
    protected static array $studlyCache = [];
82
83
    /**
84
     * Convert an UTF-8 value to ASCII.
85
     * @param string $value
86
     * @return string
87
     */
88
    public static function toAscii(string $value): string
89
    {
90
        foreach (self::getChars() as $key => $val) {
91
            $value = str_replace($val, $key, $value);
92
        }
93
94
        return (string)preg_replace('/[^\x20-\x7E]/u', '', $value);
95
    }
96
97
    /**
98
     * Convert to camel case
99
     * @param string $value
100
     * @param bool $lcfirst
101
     * @return string
102
     */
103
    public static function camel(string $value, bool $lcfirst = true): string
104
    {
105
        if (isset(self::$camelCache[$value])) {
106
            return self::$camelCache[$value];
107
        }
108
109
        $studly = static::studly($value);
110
        return self::$camelCache[$value] = ($lcfirst ? lcfirst($studly) : $studly);
111
    }
112
113
    /**
114
     * Convert an string to array
115
     * @param string $value
116
     * @param string $delimiter
117
     * @param int $limit
118
     * @return array<string>
119
     */
120
    public static function toArray(string $value, string $delimiter = ', ', int $limit = 0): array
121
    {
122
        $string = trim($value, $delimiter . ' ');
123
        if ($string === '') {
124
            return [];
125
        }
126
127
        $values = [];
128
        /** @var array<string> $rawList */
129
        $rawList = $limit < 1
130
                ? (array) explode($delimiter, $string)
131
                : (array) explode($delimiter, $string, $limit);
132
133
        foreach ($rawList as $val) {
134
            $val = trim($val);
135
            if ($val !== '') {
136
                $values[] = $val;
137
            }
138
        }
139
140
        return $values;
141
    }
142
143
    /**
144
     * Determine if a given string contains a given sub string.
145
     * @param string $value
146
     * @param string|array<mixed> $needles
147
     * @return bool
148
     */
149
    public static function contains(string $value, $needles): bool
150
    {
151
        if (!is_array($needles)) {
152
            $needles = [$needles];
153
        }
154
155
        foreach ($needles as $needle) {
156
            if ($needle !== '' && strpos($needle, $value) !== false) {
157
                return true;
158
            }
159
        }
160
161
        return false;
162
    }
163
164
    /**
165
     * Determine if a given string ends with a given sub string.
166
     * @param string $value
167
     * @param string|array<mixed> $needles
168
     * @return bool
169
     */
170
    public static function endsWith(string $value, $needles): bool
171
    {
172
        if (!is_array($needles)) {
173
            $needles = [$needles];
174
        }
175
176
        foreach ($needles as $needle) {
177
            if ($value === (string) substr($needle, -strlen($value))) {
178
                return true;
179
            }
180
        }
181
182
        return false;
183
    }
184
185
    /**
186
     * Determine if a given string starts with a given sub string.
187
     * @param string $value
188
     * @param string|array<mixed> $needles
189
     * @return bool
190
     */
191
    public static function startsWith(string $value, $needles): bool
192
    {
193
        if (!is_array($needles)) {
194
            $needles = [$needles];
195
        }
196
197
        foreach ($needles as $needle) {
198
            if ($needle !== '' && strpos($needle, $value) === 0) {
199
                return true;
200
            }
201
        }
202
203
        return false;
204
    }
205
206
    /**
207
     * Return the first line of multi line string
208
     * @param string $value
209
     * @return string
210
     */
211
    public static function firstLine(string $value): string
212
    {
213
        $str = trim($value);
214
215
        if ($str === '') {
216
            return '';
217
        }
218
219
        if (strpos($str, "\n") > 0) {
220
            $parts = explode("\n", $str);
221
222
            return $parts[0] ?? '';
223
        }
224
225
        return $str;
226
    }
227
228
    /**
229
     * Cap a string with a single instance of a given value.
230
     * @param string $value
231
     * @param string $cap
232
     * @return string
233
     */
234
    public static function finish(string $value, string $cap): string
235
    {
236
        $quoted = preg_quote($cap, '/');
237
238
        return (string) preg_replace('/(?:' . $quoted . ')+$/', '', $value)
239
                . $cap;
240
    }
241
242
    /**
243
     * Determine if a given string matches a given pattern.
244
     * @param string $pattern
245
     * @param string $value
246
     * @return bool
247
     */
248
    public static function is(string $pattern, string $value): bool
249
    {
250
        if ($pattern === $value) {
251
            return true;
252
        }
253
254
        $quoted = preg_quote($pattern, '#');
255
256
        // Asterisks are translated into zero-or-more regular expression wildcards
257
        // to make it convenient to check if the strings starts with the given
258
        // pattern such as "library/*", making any string check convenient.
259
        $cleanQuoted = str_replace('\*', '.*', $quoted);
260
261
        return (bool)preg_match('#^' . $cleanQuoted . '\z#', $value);
262
    }
263
264
    /**
265
     * Return the length of the given string
266
     * @param string|int $value
267
     * @param string $encode
268
     * @return int
269
     */
270
    public static function length($value, string $encode = 'UTF-8'): int
271
    {
272
        if (!is_string($value)) {
273
            $value = (string) $value;
274
        }
275
276
        $length = mb_strlen($value, $encode);
277
278
        return $length !== false ? $length : -1;
279
    }
280
281
282
    /**
283
     * Add padding to string
284
     * @param string|int $value
285
     * @param int $length
286
     * @param string $padStr
287
     * @param int $type
288
     * @return string
289
     */
290
    public static function pad(
291
        $value,
292
        int $length,
293
        string $padStr = ' ',
294
        int $type = STR_PAD_BOTH
295
    ): string {
296
        if (!is_string($value)) {
297
            $value = (string) $value;
298
        }
299
300
        return $length > 0
301
                ? str_pad($value, $length, $padStr, $type)
302
                : $value;
303
    }
304
305
    /**
306
     * Add padding to string to left
307
     * @param string|int $value
308
     * @param int $length
309
     * @param string $padStr
310
     * @return string
311
     */
312
    public static function padLeft(
313
        $value,
314
        int $length,
315
        string $padStr = ' '
316
    ): string {
317
        return self::pad($value, $length, $padStr, STR_PAD_LEFT);
318
    }
319
320
    /**
321
     * Add padding to string to right
322
     * @param string|int $value
323
     * @param int $length
324
     * @param string $padStr
325
     * @return string
326
     */
327
    public static function padRight(
328
        $value,
329
        int $length,
330
        string $padStr = ' '
331
    ): string {
332
        return self::pad($value, $length, $padStr, STR_PAD_RIGHT);
333
    }
334
335
    /**
336
     * Repeat the given string $length times
337
     * @param string|int $value
338
     * @param int $length
339
     * @return string
340
     */
341
    public static function repeat($value, int $length = 1): string
342
    {
343
        if (!is_string($value)) {
344
            $value = (string) $value;
345
        }
346
347
        return str_repeat($value, $length);
348
    }
349
350
    /**
351
     * Limit the length of given string
352
     * @param string $value
353
     * @param int $length
354
     * @param string $end
355
     * @return string
356
     */
357
    public static function limit(string $value, int $length = 100, string $end = '...'): string
358
    {
359
        if (mb_strwidth($value, 'UTF-8') <= $length) {
360
            return $value;
361
        }
362
363
        return rtrim(mb_strimwidth($value, 0, $length, '', 'UTF-8')) . $end;
364
    }
365
366
    /**
367
     * Limit the number of words in a string.
368
     * @param string $value
369
     * @param int $length
370
     * @param string $end
371
     * @return string
372
     */
373
    public static function words(string $value, int $length = 100, string $end = '...'): string
374
    {
375
        $matches = [];
376
        preg_match('/^\s*+(?:\S++\s*+){1,' . $length . '}/u', $value, $matches);
377
378
        if (!isset($matches[0]) || strlen($value) === strlen($matches[0])) {
379
            return $value;
380
        }
381
382
        return rtrim($matches[0]) . $end;
383
    }
384
385
    /**
386
     * Replace the first match of the given string
387
     * @param string $search
388
     * @param string $replace
389
     * @param string $value
390
     * @return string
391
     */
392
    public static function replaceFirst(string $search, string $replace, string $value): string
393
    {
394
        $pos = strpos($value, $search);
395
        if ($pos !== false) {
396
            return substr_replace($value, $replace, $pos, strlen($search));
0 ignored issues
show
Bug Best Practice introduced by
The expression return substr_replace($v... $pos, strlen($search)) could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
397
        }
398
399
        return $value;
400
    }
401
402
    /**
403
     * Replace the last match of the given string
404
     * @param string $search
405
     * @param string $replace
406
     * @param string $value
407
     * @return string
408
     */
409
    public static function replaceLast(string $search, string $replace, string $value): string
410
    {
411
        $pos = strrpos($value, $search);
412
413
        if ($pos !== false) {
414
            return substr_replace($value, $replace, $pos, strlen($search));
0 ignored issues
show
Bug Best Practice introduced by
The expression return substr_replace($v... $pos, strlen($search)) could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
415
        }
416
417
        return $value;
418
    }
419
420
    /**
421
     * Put the string to title format
422
     * @param string $value
423
     * @return string
424
     */
425
    public static function title(string $value): string
426
    {
427
        return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
428
    }
429
430
    /**
431
     * Generate a friendly "slug" from a given string.
432
     * @param string $value
433
     * @param string $separator
434
     * @return string
435
     */
436
    public static function slug(string $value, string $separator = '-'): string
437
    {
438
        $title = self::toAscii($value);
439
440
        // Convert all dashes/underscores into separator
441
        $flip = $separator === '-' ? '_' : '-';
442
443
        $utf8 = (string) preg_replace('![' . preg_quote($flip) . ']+!u', $separator, $title);
444
445
        // Remove all characters that are not the separator, letters, numbers,
446
        // or whitespace.
447
        $alphaNum = (string) preg_replace(
448
            '![^' . preg_quote($separator) . '\pL\pN\s]+!u',
449
            '',
450
            mb_strtolower($utf8)
451
        );
452
453
        // Replace all separator characters and whitespace by a single separator
454
        $removeWhitespace = (string) preg_replace(
455
            '![' . preg_quote($separator) . '\s]+!u',
456
            $separator,
457
            $alphaNum
458
        );
459
460
        return trim($removeWhitespace, $separator);
461
    }
462
463
    /**
464
     * Convert a string to snake case.
465
     * @param string $value
466
     * @param string $separator
467
     * @return string
468
     */
469
    public static function snake(string $value, string $separator = '_'): string
470
    {
471
        $key = $value . $separator;
472
        if (isset(self::$snakeCache[$key])) {
473
            return self::$snakeCache[$key];
474
        }
475
476
        if (!ctype_lower($value)) {
477
            $replace = (string) preg_replace('/\s+/', '', $value);
478
479
            $value = strtolower((string) preg_replace(
480
                '/(.)(?=[A-Z])/',
481
                '$1' . $separator,
482
                $replace
483
            ));
484
        }
485
486
        return self::$snakeCache[$key] = $value;
487
    }
488
489
    /**
490
     * Convert a value to studly caps case.
491
     * @param string $value
492
     * @return string
493
     */
494
    public static function studly(string $value): string
495
    {
496
        $key = $value;
497
        if (isset(self::$studlyCache[$key])) {
498
            return self::$studlyCache[$key];
499
        }
500
501
        $val = ucwords(str_replace(['-', '_'], ' ', $value));
502
503
        return self::$studlyCache[$key] = str_replace(' ', '', $val);
504
    }
505
506
    /**
507
     * Returns the portion of string specified by the start and
508
     * length parameters.
509
     *
510
     * @param string $value
511
     * @param int $start
512
     * @param int|null $length
513
     * @return string
514
     */
515
    public static function substr(string $value, int $start = 0, ?int $length = null): string
516
    {
517
        return mb_substr($value, $start, $length, 'UTF-8');
518
    }
519
520
    /**
521
     * Make a string's first character to upper case.
522
     * @param string $value
523
     * @return string
524
     */
525
    public static function ucfirst(string $value): string
526
    {
527
        return static::upper(
528
            static::substr($value, 0, 1)
529
        ) . static::substr($value, 1);
530
    }
531
532
    /**
533
     * Split the string by length part
534
     * @param string $value
535
     * @param int $length
536
     * @return array<int, string>
537
     */
538
    public static function split(string $value, int $length = 1): array
539
    {
540
        if ($length < 1) {
541
            return [];
542
        }
543
544
        if (self::isAscii($value)) {
545
            $res = str_split($value, $length);
546
            if ($res === false) {
547
                return [];
548
            }
549
550
            return $res;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $res could return the type true which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
551
        }
552
553
        if (mb_strlen($value) <= $length) {
554
            return [$value];
555
        }
556
        $matches = [];
557
        preg_match_all(
558
            '/.{' . $length . '}|[^\x00]{1,' . $length . '}$/us',
559
            $value,
560
            $matches
561
        );
562
563
        return $matches[0];
564
    }
565
566
    /**
567
     * Check whether the given string contains only ASCII chars
568
     * @param string $value
569
     * @return bool
570
     */
571
    public static function isAscii(string $value): bool
572
    {
573
        return (bool)!preg_match('/[^\x00-\x7F]/S', $value);
574
    }
575
576
    /**
577
     * Put string to lower case
578
     * @param string $value
579
     * @return string
580
     */
581
    public static function lower(string $value): string
582
    {
583
        return mb_strtolower($value, 'UTF-8');
584
    }
585
586
    /**
587
     * Put string to upper case
588
     * @param string $value
589
     * @return string
590
     */
591
    public static function upper(string $value): string
592
    {
593
        return mb_strtoupper($value, 'UTF-8');
594
    }
595
596
    /**
597
     * Return the unique ID
598
     * @param int $length
599
     *
600
     * @return string
601
     */
602
    public static function uniqId(int $length = 13): string
603
    {
604
        $bytes = random_bytes((int) ceil($length / 2));
605
606
        return (string)substr(bin2hex($bytes), 0, $length);
607
    }
608
609
    /**
610
     * Generate random string value
611
     * @param int $length
612
     * @return string
613
     */
614
    public static function random(int $length = 16): string
615
    {
616
        $string = '';
617
        while (($len = strlen($string)) < $length) {
618
            $size = $length - $len;
619
            $bytes = random_bytes($size);
620
621
            $string .= substr(
622
                str_replace(['/', '+', '='], '', base64_encode($bytes)),
623
                0,
624
                $size
625
            );
626
        }
627
628
        return $string;
629
    }
630
631
    /**
632
     * Generates a random string of a given type and length. Possible
633
     * values for the first argument ($type) are:
634
     *  - alnum    - alpha-numeric characters (including capitals)
635
     *  - alpha    - alphabetical characters (including capitals)
636
     *  - hexdec   - hexadecimal characters, 0-9 plus a-f
637
     *  - numeric  - digit characters, 0-9
638
     *  - nozero   - digit characters, 1-9
639
     *  - distinct - clearly distinct alpha-numeric characters.
640
     * @param string $type
641
     * @param int $length
642
     * @return string
643
     */
644
    public static function randomString(string $type = 'alnum', int $length = 8): string
645
    {
646
        $utf8 = false;
647
648
        switch ($type) {
649
            case 'alnum':
650
                $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
651
                break;
652
            case 'alpha':
653
                $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
654
                break;
655
            case 'lowalnum':
656
                $pool = '0123456789abcdefghijklmnopqrstuvwxyz';
657
                break;
658
            case 'hexdec':
659
                $pool = '0123456789abcdef';
660
                break;
661
            case 'numeric':
662
                $pool = '0123456789';
663
                break;
664
            case 'nozero':
665
                $pool = '123456789';
666
                break;
667
            case 'distinct':
668
                $pool = '2345679ACDEFHJKLMNPRSTUVWXYZ';
669
                break;
670
            default:
671
                $pool = (string)$type;
672
                $utf8 = !self::isAscii($pool);
673
                break;
674
        }
675
676
        // Split the pool into an array of characters
677
        $pool = $utf8 ? self::split($pool, 1) : str_split($pool, 1);
678
        // Largest pool key
679
        $max = count($pool) - 1;
0 ignored issues
show
Bug introduced by
It seems like $pool can also be of type boolean; however, parameter $value of count() does only seem to accept Countable|array, 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

679
        $max = count(/** @scrutinizer ignore-type */ $pool) - 1;
Loading history...
680
681
        $str = '';
682
        for ($i = 0; $i < $length; $i++) {
683
            // Select a random character from the pool and add it to the string
684
            $str .= $pool[random_int(0, $max)];
685
        }
686
687
        // Make sure alnum strings contain at least one letter and one digit
688
        if ($type === 'alnum' && $length > 1) {
689
            if (ctype_alpha($str)) {
690
                // Add a random digit
691
                $str[random_int(0, $length - 1)] = chr(random_int(48, 57));
692
            } elseif (ctype_digit($str)) {
693
                // Add a random letter
694
                $str[random_int(0, $length - 1)] = chr(random_int(65, 90));
695
            }
696
        }
697
698
        return $str;
699
    }
700
701
    /**
702
     * Create a simple random token-string
703
     * @param int $length
704
     * @param string $salt
705
     * @return string
706
     */
707
    public static function randomToken(int $length = 24, string $salt = ''): string
708
    {
709
        $string = '';
710
        $chars  = '0456789abc1def2ghi3jkl';
711
        $maxVal = strlen($chars) - 1;
712
713
        for ($i = 0; $i < $length; ++$i) {
714
            $string .= $chars[random_int(0, $maxVal)];
715
        }
716
717
        return md5($string . $salt);
718
    }
719
720
    /**
721
     * Convert the given value to string representation
722
     * @param mixed $value
723
     * @return string
724
     */
725
    public static function stringify($value): string
726
    {
727
        if ($value === null) {
728
            return 'null';
729
        }
730
731
        if (is_bool($value)) {
732
            return $value ? 'true' : 'false';
733
        }
734
735
        if (is_string($value)) {
736
            return sprintf('"%s"', $value);
737
        }
738
739
        if (is_scalar($value)) {
740
            return (string) $value;
741
        }
742
743
        if (is_array($value)) {
744
            return self::stringifyArray($value);
745
        }
746
747
        if (is_object($value)) {
748
            return self::stringifyObject($value);
749
        }
750
751
        if (is_resource($value)) {
752
            return sprintf('resource<%s>', get_resource_type($value));
753
        }
754
755
        return gettype($value);
756
    }
757
758
    /**
759
     * Convert the given array to string representation
760
     * @param array<mixed> $value
761
     * @return string
762
     */
763
    public static function stringifyArray(array $value): string
764
    {
765
        if (empty($value)) {
766
            return '[]';
767
        }
768
769
        $keys = array_keys($value);
770
        $values = array_values($value);
771
        [$firstKey] = $keys;
772
        $ignoreKeys = $firstKey === 0;
773
774
        return sprintf('[%s]', implode(', ', array_map(
775
            function ($key, $value) use ($ignoreKeys) {
776
                return $ignoreKeys
777
                        ? self::stringify($value)
778
                        : sprintf(
779
                            '%s => %s',
780
                            self::stringify($key),
781
                            self::stringify($value)
782
                        );
783
            },
784
            $keys,
785
            $values
786
        )));
787
    }
788
789
    /**
790
     * Convert the given object to string representation
791
     * @param object $value
792
     * @return string
793
     */
794
    public static function stringifyObject(object $value): string
795
    {
796
        $valueClass = get_class($value);
797
798
        if ($value instanceof Throwable) {
799
            return sprintf(
800
                '%s { "%s", %s, %s #%s }',
801
                $valueClass,
802
                $value->getMessage(),
803
                $value->getCode(),
804
                $value->getFile(),
805
                $value->getLine()
806
            );
807
        }
808
809
        if (method_exists($value, '__toString')) {
810
            return sprintf('%s { %s }', $valueClass, $value->__toString());
811
        }
812
813
        if (method_exists($value, 'toString')) {
814
            return sprintf('%s { %s }', $valueClass, $value->toString());
815
        }
816
817
        if ($value instanceof Traversable) {
818
            return sprintf(
819
                '%s %s',
820
                $valueClass,
821
                self::stringifyArray(iterator_to_array($value))
822
            );
823
        }
824
825
        if ($value instanceof DateTimeInterface) {
826
            return sprintf(
827
                '%s { %s }',
828
                $valueClass,
829
                $value->format(DateTime::ATOM)
830
            );
831
        }
832
833
        if ($value instanceof JsonSerializable) {
834
            return sprintf(
835
                '%s {%s}',
836
                $valueClass,
837
                trim((string) json_encode($value->jsonSerialize()), '{}')
838
            );
839
        }
840
841
        return $valueClass;
842
    }
843
844
    /**
845
     * Return the ASCII replacement
846
     * @return array<string, array<string>>
847
     */
848
    private static function getChars(): array
849
    {
850
        return [
851
            '0'    => ['°', '₀'],
852
            '1'    => ['¹', '₁'],
853
            '2'    => ['²', '₂'],
854
            '3'    => ['³', '₃'],
855
            '4'    => ['⁴', '₄'],
856
            '5'    => ['⁵', '₅'],
857
            '6'    => ['⁶', '₆'],
858
            '7'    => ['⁷', '₇'],
859
            '8'    => ['⁸', '₈'],
860
            '9'    => ['⁹', '₉'],
861
            'a'    => [
862
                'à',
863
                'á',
864
                'ả',
865
                'ã',
866
                'ạ',
867
                'ă',
868
                'ắ',
869
                'ằ',
870
                'ẳ',
871
                'ẵ',
872
                'ặ',
873
                'â',
874
                'ấ',
875
                'ầ',
876
                'ẩ',
877
                'ẫ',
878
                'ậ',
879
                'ā',
880
                'ą',
881
                'å',
882
                'α',
883
                'ά',
884
                'ἀ',
885
                'ἁ',
886
                'ἂ',
887
                'ἃ',
888
                'ἄ',
889
                'ἅ',
890
                'ἆ',
891
                'ἇ',
892
                'ᾀ',
893
                'ᾁ',
894
                'ᾂ',
895
                'ᾃ',
896
                'ᾄ',
897
                'ᾅ',
898
                'ᾆ',
899
                'ᾇ',
900
                'ὰ',
901
                'ά',
902
                'ᾰ',
903
                'ᾱ',
904
                'ᾲ',
905
                'ᾳ',
906
                'ᾴ',
907
                'ᾶ',
908
                'ᾷ',
909
                'а',
910
                'أ',
911
                'အ',
912
                'ာ',
913
                'ါ',
914
                'ǻ',
915
                'ǎ',
916
                'ª',
917
                'ა',
918
                'अ'
919
            ],
920
            'b'    => ['б', 'β', 'Ъ', 'Ь', 'ب', 'ဗ', 'ბ'],
921
            'c'    => ['ç', 'ć', 'č', 'ĉ', 'ċ'],
922
            'd'    => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ'],
923
            'e'    => [
924
                'é',
925
                'è',
926
                'ẻ',
927
                'ẽ',
928
                'ẹ',
929
                'ê',
930
                'ế',
931
                'ề',
932
                'ể',
933
                'ễ',
934
                'ệ',
935
                'ë',
936
                'ē',
937
                'ę',
938
                'ě',
939
                'ĕ',
940
                'ė',
941
                'ε',
942
                'έ',
943
                'ἐ',
944
                'ἑ',
945
                'ἒ',
946
                'ἓ',
947
                'ἔ',
948
                'ἕ',
949
                'ὲ',
950
                'έ',
951
                'е',
952
                'ё',
953
                'э',
954
                'є',
955
                'ə',
956
                'ဧ',
957
                'ေ',
958
                'ဲ',
959
                'ე',
960
                'ए'
961
            ],
962
            'f'    => ['ф', 'φ', 'ف', 'ƒ', 'ფ'],
963
            'g'    => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ج', 'ဂ', 'გ'],
964
            'h'    => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ'],
965
            'i'    => [
966
                'í',
967
                'ì',
968
                'ỉ',
969
                'ĩ',
970
                'ị',
971
                'î',
972
                'ï',
973
                'ī',
974
                'ĭ',
975
                'į',
976
                'ı',
977
                'ι',
978
                'ί',
979
                'ϊ',
980
                'ΐ',
981
                'ἰ',
982
                'ἱ',
983
                'ἲ',
984
                'ἳ',
985
                'ἴ',
986
                'ἵ',
987
                'ἶ',
988
                'ἷ',
989
                'ὶ',
990
                'ί',
991
                'ῐ',
992
                'ῑ',
993
                'ῒ',
994
                'ΐ',
995
                'ῖ',
996
                'ῗ',
997
                'і',
998
                'ї',
999
                'и',
1000
                'ဣ',
1001
                'ိ',
1002
                'ီ',
1003
                'ည်',
1004
                'ǐ',
1005
                'ი',
1006
                'इ'
1007
            ],
1008
            'j'    => ['ĵ', 'ј', 'Ј', 'ჯ'],
1009
            'k'    => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ'],
1010
            'l'    => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ'],
1011
            'm'    => ['м', 'μ', 'م', 'မ', 'მ'],
1012
            'n'    => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ'],
1013
            'o'    => [
1014
                'ó',
1015
                'ò',
1016
                'ỏ',
1017
                'õ',
1018
                'ọ',
1019
                'ô',
1020
                'ố',
1021
                'ồ',
1022
                'ổ',
1023
                'ỗ',
1024
                'ộ',
1025
                'ơ',
1026
                'ớ',
1027
                'ờ',
1028
                'ở',
1029
                'ỡ',
1030
                'ợ',
1031
                'ø',
1032
                'ō',
1033
                'ő',
1034
                'ŏ',
1035
                'ο',
1036
                'ὀ',
1037
                'ὁ',
1038
                'ὂ',
1039
                'ὃ',
1040
                'ὄ',
1041
                'ὅ',
1042
                'ὸ',
1043
                'ό',
1044
                'о',
1045
                'و',
1046
                'θ',
1047
                'ို',
1048
                'ǒ',
1049
                'ǿ',
1050
                'º',
1051
                'ო',
1052
                'ओ'
1053
            ],
1054
            'p'    => ['п', 'π', 'ပ', 'პ'],
1055
            'q'    => ['ყ'],
1056
            'r'    => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ'],
1057
            's'    => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს'],
1058
            't'    => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ'],
1059
            'u'    => [
1060
                'ú',
1061
                'ù',
1062
                'ủ',
1063
                'ũ',
1064
                'ụ',
1065
                'ư',
1066
                'ứ',
1067
                'ừ',
1068
                'ử',
1069
                'ữ',
1070
                'ự',
1071
                'û',
1072
                'ū',
1073
                'ů',
1074
                'ű',
1075
                'ŭ',
1076
                'ų',
1077
                'µ',
1078
                'у',
1079
                'ဉ',
1080
                'ု',
1081
                'ူ',
1082
                'ǔ',
1083
                'ǖ',
1084
                'ǘ',
1085
                'ǚ',
1086
                'ǜ',
1087
                'უ',
1088
                'उ'
1089
            ],
1090
            'v'    => ['в', 'ვ', 'ϐ'],
1091
            'w'    => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ'],
1092
            'x'    => ['χ', 'ξ'],
1093
            'y'    => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ'],
1094
            'z'    => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ'],
1095
            'aa'   => ['ع', 'आ'],
1096
            'ae'   => ['ä', 'æ', 'ǽ'],
1097
            'ai'   => ['ऐ'],
1098
            'at'   => ['@'],
1099
            'ch'   => ['ч', 'ჩ', 'ჭ'],
1100
            'dj'   => ['ђ', 'đ'],
1101
            'dz'   => ['џ', 'ძ'],
1102
            'ei'   => ['ऍ'],
1103
            'gh'   => ['غ', 'ღ'],
1104
            'ii'   => ['ई'],
1105
            'ij'   => ['ij'],
1106
            'kh'   => ['х', 'خ', 'ხ'],
1107
            'lj'   => ['љ'],
1108
            'nj'   => ['њ'],
1109
            'oe'   => ['ö', 'œ'],
1110
            'oi'   => ['ऑ'],
1111
            'oii'  => ['ऒ'],
1112
            'ps'   => ['ψ'],
1113
            'sh'   => ['ш', 'შ'],
1114
            'shch' => ['щ'],
1115
            'ss'   => ['ß'],
1116
            'sx'   => ['ŝ'],
1117
            'th'   => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'],
1118
            'ts'   => ['ц', 'ც', 'წ'],
1119
            'ue'   => ['ü'],
1120
            'uu'   => ['ऊ'],
1121
            'ya'   => ['я'],
1122
            'yu'   => ['ю'],
1123
            'zh'   => ['ж', 'ჟ'],
1124
            '(c)'  => ['©'],
1125
            'A'    => [
1126
                'Á',
1127
                'À',
1128
                'Ả',
1129
                'Ã',
1130
                'Ạ',
1131
                'Ă',
1132
                'Ắ',
1133
                'Ằ',
1134
                'Ẳ',
1135
                'Ẵ',
1136
                'Ặ',
1137
                'Â',
1138
                'Ấ',
1139
                'Ầ',
1140
                'Ẩ',
1141
                'Ẫ',
1142
                'Ậ',
1143
                'Å',
1144
                'Ā',
1145
                'Ą',
1146
                'Α',
1147
                'Ά',
1148
                'Ἀ',
1149
                'Ἁ',
1150
                'Ἂ',
1151
                'Ἃ',
1152
                'Ἄ',
1153
                'Ἅ',
1154
                'Ἆ',
1155
                'Ἇ',
1156
                'ᾈ',
1157
                'ᾉ',
1158
                'ᾊ',
1159
                'ᾋ',
1160
                'ᾌ',
1161
                'ᾍ',
1162
                'ᾎ',
1163
                'ᾏ',
1164
                'Ᾰ',
1165
                'Ᾱ',
1166
                'Ὰ',
1167
                'Ά',
1168
                'ᾼ',
1169
                'А',
1170
                'Ǻ',
1171
                'Ǎ'
1172
            ],
1173
            'B'    => ['Б', 'Β', 'ब'],
1174
            'C'    => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ'],
1175
            'D'    => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ'],
1176
            'E'    => [
1177
                'É',
1178
                'È',
1179
                'Ẻ',
1180
                'Ẽ',
1181
                'Ẹ',
1182
                'Ê',
1183
                'Ế',
1184
                'Ề',
1185
                'Ể',
1186
                'Ễ',
1187
                'Ệ',
1188
                'Ë',
1189
                'Ē',
1190
                'Ę',
1191
                'Ě',
1192
                'Ĕ',
1193
                'Ė',
1194
                'Ε',
1195
                'Έ',
1196
                'Ἐ',
1197
                'Ἑ',
1198
                'Ἒ',
1199
                'Ἓ',
1200
                'Ἔ',
1201
                'Ἕ',
1202
                'Έ',
1203
                'Ὲ',
1204
                'Е',
1205
                'Ё',
1206
                'Э',
1207
                'Є',
1208
                'Ə'
1209
            ],
1210
            'F'    => ['Ф', 'Φ'],
1211
            'G'    => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ'],
1212
            'H'    => ['Η', 'Ή', 'Ħ'],
1213
            'I'    => [
1214
                'Í',
1215
                'Ì',
1216
                'Ỉ',
1217
                'Ĩ',
1218
                'Ị',
1219
                'Î',
1220
                'Ï',
1221
                'Ī',
1222
                'Ĭ',
1223
                'Į',
1224
                'İ',
1225
                'Ι',
1226
                'Ί',
1227
                'Ϊ',
1228
                'Ἰ',
1229
                'Ἱ',
1230
                'Ἳ',
1231
                'Ἴ',
1232
                'Ἵ',
1233
                'Ἶ',
1234
                'Ἷ',
1235
                'Ῐ',
1236
                'Ῑ',
1237
                'Ὶ',
1238
                'Ί',
1239
                'И',
1240
                'І',
1241
                'Ї',
1242
                'Ǐ',
1243
                'ϒ'
1244
            ],
1245
            'K'    => ['К', 'Κ'],
1246
            'L'    => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल'],
1247
            'M'    => ['М', 'Μ'],
1248
            'N'    => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν'],
1249
            'O'    => [
1250
                'Ó',
1251
                'Ò',
1252
                'Ỏ',
1253
                'Õ',
1254
                'Ọ',
1255
                'Ô',
1256
                'Ố',
1257
                'Ồ',
1258
                'Ổ',
1259
                'Ỗ',
1260
                'Ộ',
1261
                'Ơ',
1262
                'Ớ',
1263
                'Ờ',
1264
                'Ở',
1265
                'Ỡ',
1266
                'Ợ',
1267
                'Ø',
1268
                'Ō',
1269
                'Ő',
1270
                'Ŏ',
1271
                'Ο',
1272
                'Ό',
1273
                'Ὀ',
1274
                'Ὁ',
1275
                'Ὂ',
1276
                'Ὃ',
1277
                'Ὄ',
1278
                'Ὅ',
1279
                'Ὸ',
1280
                'Ό',
1281
                'О',
1282
                'Θ',
1283
                'Ө',
1284
                'Ǒ',
1285
                'Ǿ'
1286
            ],
1287
            'P'    => ['П', 'Π'],
1288
            'R'    => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ'],
1289
            'S'    => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ'],
1290
            'T'    => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ'],
1291
            'U'    => [
1292
                'Ú',
1293
                'Ù',
1294
                'Ủ',
1295
                'Ũ',
1296
                'Ụ',
1297
                'Ư',
1298
                'Ứ',
1299
                'Ừ',
1300
                'Ử',
1301
                'Ữ',
1302
                'Ự',
1303
                'Û',
1304
                'Ū',
1305
                'Ů',
1306
                'Ű',
1307
                'Ŭ',
1308
                'Ų',
1309
                'У',
1310
                'Ǔ',
1311
                'Ǖ',
1312
                'Ǘ',
1313
                'Ǚ',
1314
                'Ǜ'
1315
            ],
1316
            'V'    => ['В'],
1317
            'W'    => ['Ω', 'Ώ', 'Ŵ'],
1318
            'X'    => ['Χ', 'Ξ'],
1319
            'Y'    => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ'],
1320
            'Z'    => ['Ź', 'Ž', 'Ż', 'З', 'Ζ'],
1321
            'AE'   => ['Ä', 'Æ', 'Ǽ'],
1322
            'CH'   => ['Ч'],
1323
            'DJ'   => ['Ђ'],
1324
            'DZ'   => ['Џ'],
1325
            'GX'   => ['Ĝ'],
1326
            'HX'   => ['Ĥ'],
1327
            'IJ'   => ['IJ'],
1328
            'JX'   => ['Ĵ'],
1329
            'KH'   => ['Х'],
1330
            'LJ'   => ['Љ'],
1331
            'NJ'   => ['Њ'],
1332
            'OE'   => ['Ö', 'Œ'],
1333
            'PS'   => ['Ψ'],
1334
            'SH'   => ['Ш'],
1335
            'SHCH' => ['Щ'],
1336
            'SS'   => ['ẞ'],
1337
            'TH'   => ['Þ'],
1338
            'TS'   => ['Ц'],
1339
            'UE'   => ['Ü'],
1340
            'YA'   => ['Я'],
1341
            'YU'   => ['Ю'],
1342
            'ZH'   => ['Ж'],
1343
            ' '    => [
1344
                "\xC2\xA0",
1345
                "\xE2\x80\x80",
1346
                "\xE2\x80\x81",
1347
                "\xE2\x80\x82",
1348
                "\xE2\x80\x83",
1349
                "\xE2\x80\x84",
1350
                "\xE2\x80\x85",
1351
                "\xE2\x80\x86",
1352
                "\xE2\x80\x87",
1353
                "\xE2\x80\x88",
1354
                "\xE2\x80\x89",
1355
                "\xE2\x80\x8A",
1356
                "\xE2\x80\xAF",
1357
                "\xE2\x81\x9F",
1358
                "\xE3\x80\x80"
1359
            ],
1360
        ];
1361
    }
1362
}
1363