14
14
final class EmojiTransliterator extends \Transliterator
15
15
{
16
16
private array $ map ;
17
+ private \Transliterator $ transliterator ;
17
18
18
19
public static function create (string $ id , int $ direction = self ::FORWARD ): ?\Transliterator
19
20
{
20
21
if (self ::REVERSE === $ direction ) {
21
- return \Transliterator::createFromRules ('A > B ' )->createInverse ();
22
+ // Create a failing reverse-transliterator to populate intl_get_error_*()
23
+ \Transliterator::createFromRules ('A > B ' )->createInverse ();
24
+
25
+ throw new \IntlException (intl_get_error_message (), intl_get_error_code ());
22
26
}
23
27
$ id = strtolower ($ id );
24
28
25
29
if (!preg_match ('/^[a-z0-9@_ \\. \\-]*$/ ' , $ id )) {
26
- return \Transliterator::create ($ id );
30
+ \Transliterator::create ('Emoji: ' .$ id );
31
+
32
+ throw new \IntlException (intl_get_error_message (), intl_get_error_code ());
27
33
}
28
34
29
35
if (!is_file (\dirname (__DIR__ )."/Resources/data/transliterator/emoji/ {$ id }.php " )) {
30
- return \Transliterator::create ($ id );
36
+ // Populate intl_get_error_*()
37
+ \Transliterator::create ('Emoji: ' .$ id );
38
+
39
+ throw new \IntlException (intl_get_error_message (), intl_get_error_code ());
31
40
}
32
41
42
+ static $ maps ;
43
+
44
+ // Create an instance of \Transliterator with a custom id; that's the only way
33
45
$ instance = unserialize (sprintf ('O:%d:"%s":1:{s:2:"id";s:%d:"%s";} ' , \strlen (self ::class), self ::class, \strlen ($ id ), $ id ));
34
- $ instance ->map = require \dirname (__DIR__ )."/Resources/data/transliterator/emoji/ {$ id }.php " ;
46
+ $ instance ->map = $ maps [ $ id ] ??= require \dirname (__DIR__ )."/Resources/data/transliterator/emoji/ {$ id }.php " ;
35
47
36
48
return $ instance ;
37
49
}
38
50
39
51
public function createInverse (): ?self
40
52
{
53
+ // Create a failing reverse-transliterator to populate intl_get_error_*()
41
54
return \Transliterator::createFromRules ('A > B ' )->createInverse ();
42
55
}
43
56
44
57
public function getErrorCode (): int |false
45
58
{
46
- return $ this ->transliterator ?->getErrorCode() ?? false ;
59
+ return $ this ->transliterator ?->getErrorCode() ?? 0 ;
47
60
}
48
61
49
62
public function getErrorMessage (): string |false
50
63
{
51
- return $ this ->transliterator ->getErrorMessage () ?? false ;
64
+ return $ this ->transliterator ? ->getErrorMessage() ?? false ;
52
65
}
53
66
54
67
public static function listIDs (): array |false
@@ -68,18 +81,20 @@ public static function listIDs(): array|false
68
81
69
82
public function transliterate (string $ string , int $ start = 0 , int $ end = -1 ): string |false
70
83
{
84
+ // Here we rely on intl to validate the $string, $start and $end arguments
85
+ // and to slice the string. Slicing is done by replacing the part if $string
86
+ // between $start and $end by a unique cookie that can be reliably used to
87
+ // identify which part of $string should be transliterated.
88
+
71
89
static $ cookie ;
72
90
static $ transliterator ;
73
91
74
92
$ cookie ??= md5 (random_bytes (8 ));
75
- $ transliterator ??= \Transliterator::createFromRules ('[:any:]* > ' .$ cookie );
93
+ $ this -> transliterator ??= clone $ transliterator ??= \Transliterator::createFromRules ('[:any:]* > ' .$ cookie );
76
94
77
- if (false === $ result = $ transliterator ->transliterate ($ string , $ start , $ end )) {
95
+ if (false === $ result = $ this -> transliterator ->transliterate ($ string , $ start , $ end )) {
78
96
return false ;
79
97
}
80
- if (4 > \strlen ($ string )) {
81
- return $ string ;
82
- }
83
98
$ map = $ this ->map ;
84
99
85
100
if ($ cookie === $ result ) {
0 commit comments