Test Setup Failed
Pull Request — latest (#3)
by Mark
65:38 queued 30:20
created

Parser::parseEmoji()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 17
c 1
b 0
f 0
dl 0
loc 30
rs 8.8333
cc 7
nc 7
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace UnicornFail\Emoji\Parser;
6
7
use UnicornFail\Emoji\Environment\EmojiEnvironmentInterface;
8
use UnicornFail\Emoji\Environment\Environment;
9
use UnicornFail\Emoji\Event\DocumentParsedEvent;
10
use UnicornFail\Emoji\Event\DocumentPreParsedEvent;
11
use UnicornFail\Emoji\Input\Input;
12
use UnicornFail\Emoji\Node\Block\Document;
13
use UnicornFail\Emoji\Node\Inline\Emoticon;
14
use UnicornFail\Emoji\Node\Inline\HtmlEntity;
15
use UnicornFail\Emoji\Node\Inline\Shortcode;
16
use UnicornFail\Emoji\Node\Inline\Text;
17
use UnicornFail\Emoji\Node\Inline\Unicode;
18
19
class Parser implements ParserInterface
20
{
21
    public const T_DATASETS = [
22
        Lexer::T_EMOTICON => 'emoticon',
23
        Lexer::T_HTML_ENTITY => 'htmlEntity',
24
        Lexer::T_SHORTCODE => 'shortcodes',
25
        Lexer::T_UNICODE => 'unicode',
26
    ];
27
28
    /** @var EmojiEnvironmentInterface  */
29
    private $environment;
30
31
    /** @var Lexer */
32
    private $lexer;
33
34
    public function __construct(?EmojiEnvironmentInterface $environment, ?Lexer $lexer = null)
35
    {
36
        $this->environment = $environment ?? Environment::create();
37
        $this->lexer       = $lexer ?? new Lexer($this->environment);
38
    }
39
40
    public function parse(string $input): Document
41
    {
42
        $preParsedEvent = new DocumentPreParsedEvent(new Document(), new Input($input));
43
        $this->environment->dispatch($preParsedEvent);
44
45
        $document = $preParsedEvent->getDocument();
46
        $input    = $preParsedEvent->getInput();
47
48
        $lineCount = $input->getLineCount();
49
        foreach ($input->getLines() as $lineNumber => $line) {
50
            $this->parseLine($line, $document);
51
52
            if ($lineNumber < $lineCount) {
53
                $document->appendChild(new Text("\n"));
54
            }
55
        }
56
57
        // If the original content ended with a new line, mimic the same.
58
        if (\preg_match_all('/.*\n|\r\n$/sm', $input->getContent()) === 1) {
59
            $document->appendChild(new Text("\n"));
60
        }
61
62
        $this->environment->dispatch(new DocumentParsedEvent($document));
63
64
        return $document;
65
    }
66
67
    protected function parseLine(string $line, Document $document): void
68
    {
69
        $this->lexer->setInput($line);
70
        $this->lexer->moveNext();
71
72
        while (true) {
73
            if (! $this->lexer->lookahead) {
74
                break;
75
            }
76
77
            $this->lexer->moveNext();
78
79
            $type  = (int) ($this->lexer->token['type'] ?? Lexer::T_TEXT);
80
            $value = (string) ($this->lexer->token['value'] ?? '');
81
82
            if ($node = $type === Lexer::T_TEXT ? $this->parseText($value) : $this->parseEmoji($type, $value)) {
83
                $document->appendChild($node);
84
            }
85
        }
86
    }
87
88
    /**
89
     * @return Emoticon|HtmlEntity|Shortcode|Unicode|null
90
     */
91
    protected function parseEmoji(int $type, string $value)
92
    {
93
        // Immediately return if not a valid type.
94
        if (! isset(self::T_DATASETS[$type])) {
95
            return null;
96
        }
97
98
        // Clone the configuration here. This is necessary so it can be passed to tokens,
99
        // which may be rendered at a later time; when the configuration may have changed.
100
        $environment = clone $this->environment;
101
        $dataset     = $this->environment->getDataset()->indexBy(self::T_DATASETS[$type]);
102
        $emoji       = $dataset->offsetGet($value);
103
104
        // Return if not an emoji.
105
        if (! $emoji) {
106
            return null;
107
        }
108
109
        switch ($type) {
110
            case Lexer::T_EMOTICON:
111
                return new Emoticon($value, $emoji, $environment);
112
            case Lexer::T_HTML_ENTITY:
113
                return new HtmlEntity($value, $emoji, $environment);
114
            case Lexer::T_SHORTCODE:
115
                return new Shortcode($value, $emoji, $environment);
116
            case Lexer::T_UNICODE:
117
                return new Unicode($value, $emoji, $environment);
118
        }
119
120
        return null;
121
    }
122
123
    protected function parseText(string $value): ?Text
124
    {
125
        $text = '';
126
        while (true) {
127
            $text .= $value;
128
            if ($this->lexer->lookahead === null || $this->lexer->lookahead['type'] !== Lexer::T_TEXT) {
129
                break;
130
            }
131
132
            $value = (string) ($this->lexer->lookahead['value'] ?? '');
133
134
            $this->lexer->moveNext();
135
        }
136
137
        return $text
138
            ? new Text($text)
139
            : null;
140
    }
141
}
142