Completed
Push — master ( f2d1ff...96912c )
by Josh
03:44
created

Links::parseAutomaticLinks()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 20
ccs 13
cts 13
cp 1
rs 9.8333
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 62
	public function parse()
20
	{
21 62
		if ($this->text->indexOf('](') !== false)
22
		{
23 33
			$this->parseInlineLinks();
24
		}
25 62
		if ($this->text->indexOf('<') !== false)
26
		{
27 9
			$this->parseAutomaticLinks();
28
		}
29 62
		if ($this->text->hasReferences)
30
		{
31 17
			$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 49
	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 49
		$priority = ($endLen === 1) ? 1 : -1;
50
51 49
		$tag = $this->parser->addTagPair('URL', $startPos, 1, $endPos, $endLen, $priority);
52 49
		$this->setLinkAttributes($tag, $linkInfo, 'url');
53
54
		// Overwrite the markup without touching the link's text
55 49
		$this->text->overwrite($startPos, 1);
56 49
		$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 17
	protected function getLabels()
65
	{
66 17
		preg_match_all(
67 17
			'/\\[((?:[^\\x17[\\]]|\\[[^\\x17[\\]]*\\])*)\\]/',
68 17
			$this->text,
69
			$matches,
70 17
			PREG_OFFSET_CAPTURE
71
		);
72 17
		$labels = [];
73 17
		foreach ($matches[1] as $m)
74
		{
75 17
			$labels[$m[1] - 1] = strtolower($m[0]);
76
		}
77
78 17
		return $labels;
79
	}
80
81
	/**
82
	* Parse automatic links markup
83
	*
84
	* @return void
85
	*/
86 9
	protected function parseAutomaticLinks()
87
	{
88 9
		preg_match_all(
89 9
			'/<[-+.\\w]++([:@])[^\\x17\\s>]+?(?:>|\\x1B7)/',
90 9
			$this->text,
91
			$matches,
92 9
			PREG_OFFSET_CAPTURE
93
		);
94 9
		foreach ($matches[0] as $i => $m)
95
		{
96
			// Re-escape escape sequences in automatic links
97 9
			$content  = substr($this->text->decode(str_replace("\x1B", "\\\x1B", $m[0])), 1, -1);
98 9
			$startPos = $m[1];
99 9
			$endPos   = $startPos + strlen($m[0]) - 1;
100
101 9
			$tagName  = ($matches[1][$i][0] === ':') ? 'URL' : 'EMAIL';
102 9
			$attrName = strtolower($tagName);
103
104 9
			$this->parser->addTagPair($tagName, $startPos, 1, $endPos, 1)
105 9
			             ->setAttribute($attrName, $content);
106
		}
107
	}
108
109
	/**
110
	* Parse inline links markup
111
	*
112
	* @return void
113
	*/
114 33
	protected function parseInlineLinks()
115
	{
116 33
		preg_match_all(
117 33
			'/\\[(?:[^\\x17[\\]]|\\[[^\\x17[\\]]*\\])*\\]\\(( *(?:[^\\x17\\s()]|\\([^\\x17\\s()]*\\))*(?=[ )]) *(?:"[^\\x17]*?"|\'[^\\x17]*?\'|\\([^\\x17)]*\\))? *)\\)/',
118 33
			$this->text,
119
			$matches,
120 33
			PREG_OFFSET_CAPTURE | PREG_SET_ORDER
121
		);
122 33
		foreach ($matches as $m)
123
		{
124 33
			$linkInfo = $m[1][0];
125 33
			$startPos = $m[0][1];
126 33
			$endLen   = 3 + strlen($linkInfo);
127 33
			$endPos   = $startPos + strlen($m[0][0]) - $endLen;
128
129 33
			$this->addLinkTag($startPos, $endPos, $endLen, $linkInfo);
130
		}
131
	}
132
133
	/**
134
	* Parse reference links markup
135
	*
136
	* @return void
137
	*/
138 17
	protected function parseReferenceLinks()
139
	{
140 17
		$labels = $this->getLabels();
141 17
		foreach ($labels as $startPos => $id)
142
		{
143 17
			$labelPos = $startPos + 2 + strlen($id);
144 17
			$endPos   = $labelPos - 1;
145 17
			$endLen   = 1;
146
147 17
			if ($this->text->charAt($labelPos) === ' ')
148
			{
149 5
				++$labelPos;
150
			}
151 17
			if (isset($labels[$labelPos], $this->text->linkReferences[$labels[$labelPos]]))
152
			{
153 10
				$id     = $labels[$labelPos];
154 10
				$endLen = $labelPos + 2 + strlen($id) - $endPos;
155
			}
156 17
			if (isset($this->text->linkReferences[$id]))
157
			{
158 17
				$this->addLinkTag($startPos, $endPos, $endLen, $this->text->linkReferences[$id]);
159
			}
160
		}
161
	}
162
}