Test Setup Failed
Pull Request — latest (#3)
by Mark
33:50
created

EmojiParser   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 112
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 18
eloc 54
c 0
b 0
f 0
dl 0
loc 112
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A parseEmoji() 0 13 3
A parse() 0 25 4
A parseLine() 0 26 5
A __construct() 0 4 1
A parseText() 0 19 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace UnicornFail\Emoji\Parser;
6
7
use UnicornFail\Emoji\Environment\EnvironmentInterface;
8
use UnicornFail\Emoji\Event\DocumentParsedEvent;
9
use UnicornFail\Emoji\Event\DocumentPreParsedEvent;
10
use UnicornFail\Emoji\Input\Input;
11
use UnicornFail\Emoji\Node\Document;
12
use UnicornFail\Emoji\Node\Emoji;
13
use UnicornFail\Emoji\Node\Node;
14
use UnicornFail\Emoji\Node\Text;
15
16
final class EmojiParser implements EmojiParserInterface
17
{
18
    public const INDICES = [
19
        Lexer::T_EMOTICON    => 'emoticon',
20
        Lexer::T_HTML_ENTITY => 'htmlEntity',
21
        Lexer::T_SHORTCODE   => 'shortcodes',
22
        Lexer::T_UNICODE     => 'unicode',
23
    ];
24
25
    /** @var EnvironmentInterface */
26
    private $environment;
27
28
    /** @var Lexer */
29
    private $lexer;
30
31
    public function __construct(EnvironmentInterface $environment, ?Lexer $lexer = null)
32
    {
33
        $this->environment = $environment;
34
        $this->lexer       = $lexer ?? new Lexer($environment);
35
    }
36
37
    public function parse(string $input): Document
38
    {
39
        $preParsedEvent = new DocumentPreParsedEvent(new Document(), new Input($input));
40
        $this->environment->dispatch($preParsedEvent);
41
42
        $document = $preParsedEvent->getDocument();
43
        $input    = $preParsedEvent->getInput();
44
45
        $lineCount = $input->getLineCount();
46
        foreach ($input->getLines() as $lineNumber => $line) {
47
            $this->parseLine($line, $document);
48
49
            if ($lineNumber < $lineCount) {
50
                $document->appendChild(new Text("\n"));
51
            }
52
        }
53
54
        // If the original content ended with a new line, mimic the same.
55
        if (\preg_match_all('/.*\n|\r\n$/sm', $input->getContent()) === 1) {
56
            $document->appendChild(new Text("\n"));
57
        }
58
59
        $this->environment->dispatch(new DocumentParsedEvent($document));
60
61
        return $document;
62
    }
63
64
    protected function parseLine(string $line, Document $document): void
65
    {
66
        $this->lexer->setInput($line);
67
        $this->lexer->moveNext();
68
69
        while (true) {
70
            if (! $this->lexer->lookahead) {
71
                break;
72
            }
73
74
            $this->lexer->moveNext();
75
76
            $type  = (int) ($this->lexer->token['type'] ?? Lexer::T_TEXT);
77
            $value = (string) ($this->lexer->token['value'] ?? '');
78
79
            switch ($type) {
80
                case Lexer::T_TEXT:
81
                    $node = $this->parseText($value);
82
                    break;
83
84
                default:
85
                    $node = $this->parseEmoji($type, $value);
86
            }
87
88
            if ($node instanceof Node) {
89
                $document->appendChild($node);
90
            }
91
        }
92
    }
93
94
    protected function parseEmoji(int $type, string $value): ?Emoji
95
    {
96
        // Immediately return if not a valid type.
97
        if (! isset(self::INDICES[$type])) {
98
            return null;
99
        }
100
101
        // Return if no emoji could be found.
102
        if (! ($emoji = $this->environment->getRuntimeDataset(self::INDICES[$type])->offsetGet($value))) {
103
            return null;
104
        }
105
106
        return new Emoji($type, $value, $emoji);
107
    }
108
109
    protected function parseText(string $value): ?Text
110
    {
111
        $text = '';
112
        while (true) {
113
            $text .= $value;
114
            if ($this->lexer->lookahead === null || $this->lexer->lookahead['type'] !== Lexer::T_TEXT) {
115
                break;
116
            }
117
118
            $value = (string) ($this->lexer->lookahead['value'] ?? '');
119
120
            $this->lexer->moveNext();
121
        }
122
123
        if (! $text) {
124
            return null;
125
        }
126
127
        return new Text($text);
128
    }
129
}
130