Completed
Push — master ( b95662...35899e )
by Colin
08:07 queued 06:22
created

QuoteParser::parse()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 4

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 29
ccs 19
cts 19
cp 1
rs 8.5806
cc 4
eloc 17
nc 8
nop 1
crap 4
1
<?php
2
3
/*
4
 * This file is part of the league/commonmark-extras package.
5
 *
6
 * (c) Colin O'Dell <[email protected]>
7
 *
8
 * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmark-js)
9
 *  - (c) John MacFarlane
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace League\CommonMark\Extras\SmartPunct;
16
17
use League\CommonMark\Delimiter\Delimiter;
18
use League\CommonMark\Inline\Element\Text;
19
use League\CommonMark\Inline\Parser\AbstractInlineParser;
20
use League\CommonMark\InlineParserContext;
21
use League\CommonMark\Util\RegexHelper;
22
23
class QuoteParser extends AbstractInlineParser
24
{
25
    protected $double = ['"', '“', '”'];
26
    protected $single = ["'", '‘', '’'];
27
28
    /**
29
     * @return string[]
30
     */
31 45
    public function getCharacters()
32
    {
33 45
        return array_merge($this->double, $this->single);
34
    }
35
36
    /**
37
     * @param InlineParserContext $inlineContext
38
     *
39
     * @return bool
40
     */
41 27
    public function parse(InlineParserContext $inlineContext)
42
    {
43 27
        $cursor = $inlineContext->getCursor();
44 27
        $character = $this->getCharacterType($cursor->getCharacter());
45
46 27
        $charBefore = $cursor->peek(-1);
47 27
        if ($charBefore === null) {
48 21
            $charBefore = "\n";
49 21
        }
50
51 27
        $cursor->advance();
52
53 27
        $charAfter = $cursor->getCharacter();
54 27
        if ($charAfter === null) {
55 15
            $charAfter = "\n";
56 15
        }
57
58 27
        list($leftFlanking, $rightFlanking) = $this->determineFlanking($charBefore, $charAfter);
59 27
        $canOpen = $leftFlanking && !$rightFlanking;
60 27
        $canClose = $rightFlanking;
61
62 27
        $node = new Text($character, ['delim' => true]);
63 27
        $inlineContext->getContainer()->appendChild($node);
64
65
        // Add entry to stack to this opener
66 27
        $inlineContext->getDelimiterStack()->push(new Delimiter($character, 1, $node, $canOpen, $canClose));
67
68 27
        return true;
69
    }
70
71
    /**
72
     * @param string $character
73
     *
74
     * @return string|null
75
     */
76 27
    private function getCharacterType($character)
77
    {
78 27
        if (in_array($character, $this->double)) {
79 12
            return '“';
80 24
        } elseif (in_array($character, $this->single)) {
81 24
            return '’';
82
        }
83
    }
84
85
    /**
86
     * @param string $charBefore
87
     * @param string $charAfter
88
     *
89
     * @return string[]
90
     */
91 27
    private function determineFlanking($charBefore, $charAfter)
92
    {
93 27
        $afterIsWhitespace = preg_match('/\pZ|\s/u', $charAfter);
94 27
        $afterIsPunctuation = preg_match(RegexHelper::REGEX_PUNCTUATION, $charAfter);
95 27
        $beforeIsWhitespace = preg_match('/\pZ|\s/u', $charBefore);
96 27
        $beforeIsPunctuation = preg_match(RegexHelper::REGEX_PUNCTUATION, $charBefore);
97
98 27
        $leftFlanking = !$afterIsWhitespace &&
99 27
            !($afterIsPunctuation &&
100 27
                !$beforeIsWhitespace &&
101 27
                !$beforeIsPunctuation);
102
103 27
        $rightFlanking = !$beforeIsWhitespace &&
104 27
            !($beforeIsPunctuation &&
105 27
                !$afterIsWhitespace &&
106 27
                !$afterIsPunctuation);
107
108 27
        return [$leftFlanking, $rightFlanking];
109
    }
110
}
111