Test Setup Failed
Pull Request — latest (#3)
by Mark
34:19
created

EmojiParser::getLexer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace UnicornFail\Emoji\Parser;
6
7
use UnicornFail\Emoji\Dataset\Emoji as DatasetEmoji;
8
use UnicornFail\Emoji\Environment\EnvironmentInterface;
9
use UnicornFail\Emoji\Event\DocumentParsedEvent;
10
use UnicornFail\Emoji\Event\DocumentPreParsedEvent;
11
use UnicornFail\Emoji\Lexer\EmojiLexer;
12
use UnicornFail\Emoji\Node\Document;
13
use UnicornFail\Emoji\Node\Emoji;
14
use UnicornFail\Emoji\Node\Text;
15
16
final class EmojiParser implements EmojiParserInterface
17
{
18
    public const INDICES = [
19
        EmojiLexer::T_EMOTICON    => 'emoticon',
20
        EmojiLexer::T_HTML_ENTITY => 'htmlEntity',
21
        EmojiLexer::T_SHORTCODE   => 'shortcodes',
22
        EmojiLexer::T_UNICODE     => 'unicode',
23
    ];
24
25
    /** @var EnvironmentInterface */
26
    private $environment;
27
28
    /** @var EmojiLexer */
29
    private $lexer;
30
31
    public function __construct(EnvironmentInterface $environment, ?EmojiLexer $lexer = null)
32
    {
33
        $this->environment = $environment;
34
        $this->lexer       = $lexer ?? new EmojiLexer($environment);
35
    }
36
37
    public function getLexer(): EmojiLexer
38
    {
39
        return $this->lexer;
40
    }
41
42
    public function parse(string $input): Document
43
    {
44
        $preParsedEvent = new DocumentPreParsedEvent(new Document(), $input);
45
        $this->environment->dispatch($preParsedEvent);
46
47
        $document = $preParsedEvent->getDocument();
48
        $input    = $preParsedEvent->getInput();
49
50
        $this->lexer->setInput($input);
51
        $this->lexer->moveNext();
52
53
        while (true) {
54
            if (! $this->lexer->lookahead) {
55
                break;
56
            }
57
58
            $this->lexer->moveNext();
59
60
            /** @var array<string, mixed> $token */
61
            $token = $this->lexer->token;
62
63
            $value = '';
64
            if (((string) $token['value']) !== '') {
65
                $value = (string) ($token['value'] ?? '');
66
            }
67
68
            $type = (int) $token['type'];
69
70
            $node = null;
71
            if ($type !== EmojiLexer::T_TEXT && \in_array($type, EmojiLexer::TYPES, true)) {
72
                $node = $this->parseEmoji($type, $value);
73
            }
74
75
            if ($node === null) {
76
                $node = $this->parseText($value);
77
            }
78
79
            $document->appendNode($node);
80
        }
81
82
        $this->environment->dispatch(new DocumentParsedEvent($document));
83
84
        return $document;
85
    }
86
87
    protected function parseEmoji(int $type, string $value): ?Emoji
88
    {
89
        $dataset = $this->environment->getRuntimeDataset(self::INDICES[$type]);
90
91
        try {
92
            /** @var DatasetEmoji $emoji */
93
            $emoji = $dataset->offsetGet($value);
94
95
            return new Emoji($type, $value, $emoji);
96
        } catch (\OutOfRangeException $exception) {
97
            return null;
98
        }
99
    }
100
101
    protected function parseText(string $value): Text
102
    {
103
        $text = '';
104
        while (true) {
105
            $text .= $value;
106
            if ($this->lexer->lookahead === null || $this->lexer->lookahead['type'] !== EmojiLexer::T_TEXT) {
107
                break;
108
            }
109
110
            $value = (string) ($this->lexer->lookahead['value'] ?? '');
111
112
            $this->lexer->moveNext();
113
        }
114
115
        return new Text($text);
116
    }
117
}
118