Completed
Branch wip/litedown (039d9d)
by Josh
29:41
created

ParsedText::overwrite()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 2
crap 2
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2017 The s9e Authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\TextFormatter\Plugins\Litedown\Parser;
9
10
use ArrayAccess;
11
12
class ParsedText implements ArrayAccess
13
{
14
	/**
15
	* @var bool Whether to decode HTML entities when decoding text
16
	*/
17
	public $decodeHtmlEntities = false;
18
19
	/**
20
	* @var bool Whether text contains escape characters
21
	*/
22
	protected $hasEscapedChars = false;
23
24
	/**
25
	* @var array Array of [label => link info]
26
	*/
27
	public $linkReferences = [];
28
29
	/**
30
	* @var string Text being parsed
31
	*/
32
	protected $text;
33
34
	/**
35
	* @param string $text Original text
36
	*/
37 263
	public function __construct($text)
38
	{
39 263
		if (strpos($text, '\\') !== false && preg_match('/\\\\[!"\'()*[\\\\\\]^_`~]/', $text))
40 263
		{
41 15
			$this->hasEscapedChars = true;
42
43
			// Encode escaped literals that have a special meaning otherwise, so that we don't have
44
			// to take them into account in regexps
45 15
			$text = strtr(
46 15
				$text,
47
				[
48 15
					'\\!' => "\x1B0", '\\"' => "\x1B1", "\\'" => "\x1B2", '\\('  => "\x1B3",
49 15
					'\\)' => "\x1B4", '\\*' => "\x1B5", '\\[' => "\x1B6", '\\\\' => "\x1B7",
50 15
					'\\]' => "\x1B8", '\\^' => "\x1B9", '\\_' => "\x1BA", '\\`'  => "\x1BB",
51
					'\\~' => "\x1BC"
52 15
				]
53 15
			);
54 15
		}
55
56
		// We append a couple of lines and a non-whitespace character at the end of the text in
57
		// order to trigger the closure of all open blocks such as quotes and lists
58 263
		$this->text = $text . "\n\n\x17";
59 263
	}
60
61
	/**
62
	* @return string
63
	*/
64 263
	public function __toString()
65
	{
66 263
		return $this->text;
67
	}
68
69
	/**
70
	* Decode a chunk of encoded text to be used as an attribute value
71
	*
72
	* Decodes escaped literals and removes slashes and 0x1A characters
73
	*
74
	* @param  string $str Encoded text
75
	* @return string      Decoded text
76
	*/
77 69
	public function decode($str)
78
	{
79 69
		if ($this->decodeHtmlEntities && strpos($str, '&') !== false)
80 69
		{
81 1
			$str = html_entity_decode($str, ENT_QUOTES, 'UTF-8');
82 1
		}
83 69
		$str = str_replace("\x1A", '', $str);
84
85 69
		if ($this->hasEscapedChars)
86 69
		{
87 7
			$str = strtr(
88 7
				$str,
89
				[
90 7
					"\x1B0" => '!', "\x1B1" => '"', "\x1B2" => "'", "\x1B3" => '(',
91 7
					"\x1B4" => ')', "\x1B5" => '*', "\x1B6" => '[', "\x1B7" => '\\',
92 7
					"\x1B8" => ']', "\x1B9" => '^', "\x1BA" => '_', "\x1BB" => '`',
93
					"\x1BC" => '~'
94 7
				]
95 7
			);
96 7
		}
97
98 69
		return $str;
99
	}
100
101
	/**
102
	* Test whether given position is preceded by whitespace
103
	*
104
	* @param  integer $pos
105
	* @return bool
106
	*/
107 59
	public function isAfterWhitespace($pos)
108
	{
109 59
		return ($pos > 0 && $this->isWhitespace($this->text[$pos - 1]));
110
	}
111
112
	/**
113
	* Test whether given character is alphanumeric
114
	*
115
	* @param  string $chr
116
	* @return bool
117
	*/
118 8
	public function isAlnum($chr)
119
	{
120 8
		return (strpos(' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', $chr) > 0);
121
	}
122
123
	/**
124
	* Test whether given position is followed by whitespace
125
	*
126
	* @param  integer $pos
127
	* @return bool
128
	*/
129 59
	public function isBeforeWhitespace($pos)
130
	{
131 59
		return $this->isWhitespace($this->text[$pos + 1]);
132
	}
133
134
	/**
135
	* Test whether a length of text is surrounded by alphanumeric characters
136
	*
137
	* @param  integer $pos Start of the text
138
	* @param  integer $len Length of the text
139
	* @return bool
140
	*/
141 8
	public function isSurroundedByAlnum($pos, $len)
142
	{
143 8
		return ($pos > 0 && $this->isAlnum($this->text[$pos - 1]) && $this->isAlnum($this->text[$pos + $len]));
144
	}
145
146
	/**
147
	* Test whether given character is an ASCII whitespace character
148
	*
149
	* NOTE: newlines are normalized to LF before parsing so we don't have to check for CR
150
	*
151
	* @param  string $chr
152
	* @return bool
153
	*/
154 59
	public function isWhitespace($chr)
155
	{
156 59
		return (strpos(" \n\t", $chr) !== false);
157
	}
158
159
	/**
160
	* Mark the boundary of a block in the original text
161
	*
162
	* @param  integer $pos
163
	* @return void
164
	*/
165 263
	public function markBoundary($pos)
166
	{
167 263
		$this->text[$pos] = "\x17";
168 263
	}
169
170
	/**
171
	* @param  mixed $offset
172
	* @return bool
173
	*/
174
	public function offsetExists($offset)
175
	{
176
		return isset($this->text[$offset]);
177
	}
178
179
	/**
180
	* @param  mixed  $offset
181
	* @return string
182
	*/
183 130
	public function offsetGet($offset)
184
	{
185 130
		return $this->text[$offset];
186
	}
187
188
	/**
189
	* @param  mixed  $offset
190
	* @param  string $offset
191
	* @return void
192
	*/
193
	public function offsetSet($offset, $value)
194
	{
195
		$this->text[$offset] = $value;
196
	}
197
198
	/**
199
	* @param  mixed  $offset
200
	* @return void
201
	*/
202
	public function offsetUnset($offset)
203
	{
204
		unset($this->text[$offset]);
205
	}
206
207
	/**
208
	* Overwrite part of the text with substitution characters ^Z (0x1A)
209
	*
210
	* @param  integer $pos Start of the range
211
	* @param  integer $len Length of text to overwrite
212
	* @return void
213
	*/
214 169
	public function overwrite($pos, $len)
215
	{
216 169
		if ($len > 0)
217 169
		{
218 169
			$this->text = substr($this->text, 0, $pos) . str_repeat("\x1A", $len) . substr($this->text, $pos + $len);
219 169
		}
220
	}
221
}