Completed
Branch wip/litedown (e234a3)
by Josh
31:46 queued 18:30
created

Emphasis::ignoreEmphasis()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 2
cts 2
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 2
nc 3
nop 2
crap 3
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\Passes;
9
10
class Emphasis extends AbstractPass
11
{
12
	/**
13
	* {@inheritdoc}
14
	*/
15 263
	public function parse()
16
	{
17 263
		$this->parseEmphasisByCharacter('*', '/\\*+/');
18 263
		$this->parseEmphasisByCharacter('_', '/_+/');
19 263
	}
20
21
	/**
22
	* Parse emphasis and strong applied using given character
23
	*
24
	* @param  string $character Markup character, either * or _
25
	* @param  string $regexp    Regexp used to match the series of emphasis character
26
	* @return void
27
	*/
28 263
	protected function parseEmphasisByCharacter($character, $regexp)
29
	{
30 263
		$pos = $this->text->indexOf($character);
31 263
		if ($pos === false)
32 263
		{
33 263
			return;
34
		}
35
36 60
		foreach ($this->getEmphasisByBlock($regexp, $pos) as $block)
1 ignored issue
show
Bug introduced by
It seems like $pos defined by $this->text->indexOf($character) on line 30 can also be of type boolean; however, s9e\TextFormatter\Plugin...s::getEmphasisByBlock() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
37
		{
38 60
			$this->processEmphasisBlock($block);
39 60
		}
40 60
	}
41
42
	/**
43
	* Get emphasis markup split by block
44
	*
45
	* @param  string  $regexp Regexp used to match emphasis
46
	* @param  integer $pos    Position in the text of the first emphasis character
47
	* @return array[]         Each array contains a list of [matchPos, matchLen] pairs
48
	*/
49 60
	protected function getEmphasisByBlock($regexp, $pos)
50
	{
51 60
		$block    = [];
52 60
		$blocks   = [];
53 60
		$breakPos = $this->text->indexOf("\x17", $pos);
54
55 60
		preg_match_all($regexp, $this->text, $matches, PREG_OFFSET_CAPTURE, $pos);
56 60
		foreach ($matches[0] as $m)
57
		{
58 60
			$matchPos = $m[1];
59 60
			$matchLen = strlen($m[0]);
60
61
			// Test whether we've just passed the limits of a block
62 60
			if ($matchPos > $breakPos)
63 60
			{
64 9
				$blocks[] = $block;
65 9
				$block    = [];
66 9
				$breakPos = $this->text->indexOf("\x17", $matchPos);
67 9
			}
68
69
			// Test whether we should ignore this markup
70 60
			if (!$this->ignoreEmphasis($matchPos, $matchLen))
71 60
			{
72 59
				$block[] = [$matchPos, $matchLen];
73 59
			}
74 60
		}
75 60
		$blocks[] = $block;
76
77 60
		return $blocks;
78
	}
79
80
	/**
81
	* Test whether emphasis should be ignored at the given position in the text
82
	*
83
	* @param  integer $matchPos Position of the emphasis in the text
84
	* @param  integer $matchLen Length of the emphasis
85
	* @return bool
86
	*/
87 60
	protected function ignoreEmphasis($matchPos, $matchLen)
88
	{
89
		// Ignore single underscores between alphanumeric characters
90 60
		return ($this->text->charAt($matchPos) === '_' && $matchLen === 1 && $this->text->isSurroundedByAlnum($matchPos, $matchLen));
91
	}
92
93
	/**
94
	* Process a list of emphasis markup strings
95
	*
96
	* @param  array[] $block List of [matchPos, matchLen] pairs
97
	* @return void
98
	*/
99 60
	protected function processEmphasisBlock(array $block)
100
	{
101 60
		$emPos     = null;
102 60
		$strongPos = null;
103 60
		foreach ($block as list($matchPos, $matchLen))
104
		{
105 59
			$canOpen      = !$this->text->isBeforeWhitespace($matchPos + $matchLen - 1);
106 59
			$canClose     = !$this->text->isAfterWhitespace($matchPos);
107 59
			$closeLen     = ($canClose) ? min($matchLen, 3) : 0;
108 59
			$closeEm      = ($closeLen & 1) && isset($emPos);
109 59
			$closeStrong  = ($closeLen & 2) && isset($strongPos);
110 59
			$emEndPos     = $matchPos;
111 59
			$strongEndPos = $matchPos;
112 59
			$remaining    = $matchLen;
113
114 59
			if (isset($emPos) && $emPos === $strongPos)
115 59
			{
116
				if ($closeEm)
117 13
				{
118 11
					$emPos += 2;
119 11
				}
120
				else
121
				{
122 2
					++$strongPos;
123
				}
124 13
			}
125
126 59
			if ($closeEm && $closeStrong)
127 59
			{
128 11
				if ($emPos < $strongPos)
129 11
				{
130 1
					$emEndPos += 2;
131 1
				}
132
				else
133
				{
134 10
					++$strongEndPos;
135
				}
136 11
			}
137
138
			if ($closeEm)
139 59
			{
140 34
				--$remaining;
141 34
				$this->parser->addTagPair('EM', $emPos, 1, $emEndPos, 1);
142 34
				$emPos = null;
143 34
			}
144
			if ($closeStrong)
145 59
			{
146 23
				$remaining -= 2;
147 23
				$this->parser->addTagPair('STRONG', $strongPos, 2, $strongEndPos, 2);
148 23
				$strongPos = null;
149 23
			}
150
151
			if ($canOpen)
152 59
			{
153 54
				$remaining = min($remaining, 3);
154 54
				if ($remaining & 1)
155 54
				{
156 49
					$emPos     = $matchPos + $matchLen - $remaining;
157 49
				}
158 54
				if ($remaining & 2)
159 54
				{
160 29
					$strongPos = $matchPos + $matchLen - $remaining;
161 29
				}
162 54
			}
163 60
		}
164
	}
165
}