1414final class EmojiTransliterator extends \Transliterator
1515{
1616 private array $ map ;
17+ private \Transliterator $ transliterator ;
1718
1819 public static function create (string $ id , int $ direction = self ::FORWARD ): ?\Transliterator
1920 {
2021 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 ());
2226 }
2327 $ id = strtolower ($ id );
2428
2529 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 ());
2733 }
2834
2935 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 ());
3140 }
3241
42+ static $ maps ;
43+
44+ // Create an instance of \Transliterator with a custom id; that's the only way
3345 $ 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 " ;
3547
3648 return $ instance ;
3749 }
3850
3951 public function createInverse (): ?self
4052 {
53+ // Create a failing reverse-transliterator to populate intl_get_error_*()
4154 return \Transliterator::createFromRules ('A > B ' )->createInverse ();
4255 }
4356
4457 public function getErrorCode (): int |false
4558 {
46- return $ this ->transliterator ?->getErrorCode() ?? false ;
59+ return $ this ->transliterator ?->getErrorCode() ?? 0 ;
4760 }
4861
4962 public function getErrorMessage (): string |false
5063 {
51- return $ this ->transliterator ->getErrorMessage () ?? false ;
64+ return $ this ->transliterator ? ->getErrorMessage() ?? false ;
5265 }
5366
5467 public static function listIDs (): array |false
@@ -68,18 +81,20 @@ public static function listIDs(): array|false
6881
6982 public function transliterate (string $ string , int $ start = 0 , int $ end = -1 ): string |false
7083 {
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+
7189 static $ cookie ;
7290 static $ transliterator ;
7391
7492 $ cookie ??= md5 (random_bytes (8 ));
75- $ transliterator ??= \Transliterator::createFromRules ('[:any:]* > ' .$ cookie );
93+ $ this -> transliterator ??= clone $ transliterator ??= \Transliterator::createFromRules ('[:any:]* > ' .$ cookie );
7694
77- if (false === $ result = $ transliterator ->transliterate ($ string , $ start , $ end )) {
95+ if (false === $ result = $ this -> transliterator ->transliterate ($ string , $ start , $ end )) {
7896 return false ;
7997 }
80- if (4 > \strlen ($ string )) {
81- return $ string ;
82- }
8398 $ map = $ this ->map ;
8499
85100 if ($ cookie === $ result ) {
0 commit comments