Completed
Branch 2.5/LitedownAutolink (fd0239)
by Josh
04:20
created

Links::parseAutomaticLinks()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 17
dl 0
loc 27
ccs 16
cts 16
cp 1
rs 9.7
c 0
b 0
f 0
cc 3
nc 3
nop 0
crap 3
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2020 The s9e authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\TextFormatter\Plugins\Litedown\Parser\Passes;
9
10
use s9e\TextFormatter\Plugins\Litedown\Parser\LinkAttributesSetter;
11
12
class Links extends AbstractPass
13
{
14
	use LinkAttributesSetter;
15
16
	/**
17
	* {@inheritdoc}
18
	*/
19 56
	public function parse()
20
	{
21 56
		if ($this->text->indexOf('](') !== false)
22
		{
23 31
			$this->parseInlineLinks();
24
		}
25 56
		if ($this->text->indexOf('<') !== false)
26
		{
27 6
			$this->parseAutomaticLinks();
28
		}
29 56
		if ($this->text->hasReferences)
30
		{
31 16
			$this->parseReferenceLinks();
32
		}
33
	}
34
35
	/**
36
	* Add an image tag for given text span
37
	*
38
	* @param  integer $startPos Start tag position
39
	* @param  integer $endPos   End tag position
40
	* @param  integer $endLen   End tag length
41
	* @param  string  $linkInfo    URL optionally followed by space and a title
42
	* @return void
43
	*/
44 46
	protected function addLinkTag($startPos, $endPos, $endLen, $linkInfo)
45
	{
46
		// Give the link a slightly worse priority if this is a implicit reference and a slightly
47
		// better priority if it's an explicit reference or an inline link or to give it precedence
48
		// over possible BBCodes such as [b](https://en.wikipedia.org/wiki/B)
49 46
		$priority = ($endLen === 1) ? 1 : -1;
50
51 46
		$tag = $this->parser->addTagPair('URL', $startPos, 1, $endPos, $endLen, $priority);
52 46
		$this->setLinkAttributes($tag, $linkInfo, 'url');
53
54
		// Overwrite the markup without touching the link's text
55 46
		$this->text->overwrite($startPos, 1);
56 46
		$this->text->overwrite($endPos,   $endLen);
57
	}
58
59
	/**
60
	* Capture and return labels used in current text
61
	*
62
	* @return array Labels' text position as keys, lowercased text content as values
63
	*/
64 16
	protected function getLabels()
65
	{
66 16
		preg_match_all(
67 16
			'/\\[((?:[^\\x17[\\]]|\\[[^\\x17[\\]]*\\])*)\\]/',
68 16
			$this->text,
69
			$matches,
70 16
			PREG_OFFSET_CAPTURE
71
		);
72 16
		$labels = [];
73 16
		foreach ($matches[1] as $m)
74
		{
75 16
			$labels[$m[1] - 1] = strtolower($m[0]);
76
		}
77
78 16
		return $labels;
79
	}
80
81
	/**
82
	* Parse automatic links markup
83
	*
84
	* @return void
85
	*/
86 6
	protected function parseAutomaticLinks()
87
	{
88 6
		preg_match_all(
89 6
			'/<[-+.\\w]++([:@])[^\\x17\\s>]++>/',
90 6
			$this->text,
91
			$matches,
92 6
			PREG_OFFSET_CAPTURE
93
		);
94 6
		foreach ($matches[0] as $i => $m)
95
		{
96 6
			$content  = $this->text->decode(str_replace("\x1B", "\\\x1B", substr($m[0], 1, -1)));
97 6
			$startPos = $m[1];
98 6
			$endPos   = $startPos + strlen($m[0]) - 1;
99
100 6
			if ($matches[1][$i][0] === ':')
101
			{
102 5
				$tagName  = 'URL';
103 5
				$attrName = 'url';
104
			}
105
			else
106
			{
107 1
				$tagName  = 'EMAIL';
108 1
				$attrName = 'email';
109
			}
110
111 6
			$this->parser->addTagPair($tagName, $startPos, 1, $endPos, 1)
112 6
			             ->setAttribute($attrName, $content);
113
		}
114
	}
115
116
	/**
117
	* Parse inline links markup
118
	*
119
	* @return void
120
	*/
121 31
	protected function parseInlineLinks()
122
	{
123 31
		preg_match_all(
124 31
			'/\\[(?:[^\\x17[\\]]|\\[[^\\x17[\\]]*\\])*\\]\\(( *(?:[^\\x17\\s()]|\\([^\\x17\\s()]*\\))*(?=[ )]) *(?:"[^\\x17]*?"|\'[^\\x17]*?\'|\\([^\\x17)]*\\))? *)\\)/',
125 31
			$this->text,
126
			$matches,
127 31
			PREG_OFFSET_CAPTURE | PREG_SET_ORDER
128
		);
129 31
		foreach ($matches as $m)
130
		{
131 31
			$linkInfo = $m[1][0];
132 31
			$startPos = $m[0][1];
133 31
			$endLen   = 3 + strlen($linkInfo);
134 31
			$endPos   = $startPos + strlen($m[0][0]) - $endLen;
135
136 31
			$this->addLinkTag($startPos, $endPos, $endLen, $linkInfo);
137
		}
138
	}
139
140
	/**
141
	* Parse reference links markup
142
	*
143
	* @return void
144
	*/
145 16
	protected function parseReferenceLinks()
146
	{
147 16
		$labels = $this->getLabels();
148 16
		foreach ($labels as $startPos => $id)
149
		{
150 16
			$labelPos = $startPos + 2 + strlen($id);
151 16
			$endPos   = $labelPos - 1;
152 16
			$endLen   = 1;
153
154 16
			if ($this->text->charAt($labelPos) === ' ')
155
			{
156 5
				++$labelPos;
157
			}
158 16
			if (isset($labels[$labelPos], $this->text->linkReferences[$labels[$labelPos]]))
159
			{
160 9
				$id     = $labels[$labelPos];
161 9
				$endLen = $labelPos + 2 + strlen($id) - $endPos;
162
			}
163 16
			if (isset($this->text->linkReferences[$id]))
164
			{
165 16
				$this->addLinkTag($startPos, $endPos, $endLen, $this->text->linkReferences[$id]);
166
			}
167
		}
168
	}
169
}