LinkParserHelper::manuallyParseLinkDestination()   B
last analyzed

Complexity

Conditions 10
Paths 14

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 10

Importance

Changes 0
Metric Value
dl 0
loc 37
ccs 24
cts 24
cp 1
rs 7.6666
c 0
b 0
f 0
cc 10
nc 14
nop 1
crap 10

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the league/commonmark package.
5
 *
6
 * (c) Colin O'Dell <[email protected]>
7
 *
8
 * Original code based on the CommonMark JS reference parser (https://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\Util;
16
17
use League\CommonMark\Cursor;
18
19
final class LinkParserHelper
20
{
21
    /**
22
     * Attempt to parse link destination
23
     *
24
     * @param Cursor $cursor
25
     *
26
     * @return null|string The string, or null if no match
27
     */
28 417
    public static function parseLinkDestination(Cursor $cursor): ?string
29
    {
30 417
        if ($res = $cursor->match(RegexHelper::REGEX_LINK_DESTINATION_BRACES)) {
31
            // Chop off surrounding <..>:
32 30
            return UrlEncoder::unescapeAndEncode(
33 30
                RegexHelper::unescape(\substr($res, 1, -1))
34
            );
35
        }
36
37 390
        if ($cursor->getCharacter() === '<') {
38 9
            return null;
39
        }
40
41 381
        $destination = self::manuallyParseLinkDestination($cursor);
42 381
        if ($destination === null) {
43 3
            return null;
44
        }
45
46 378
        return UrlEncoder::unescapeAndEncode(
47 378
            RegexHelper::unescape($destination)
48
        );
49
    }
50
51
    /**
52
     * @param Cursor $cursor
53
     *
54
     * @return int
55
     */
56 423
    public static function parseLinkLabel(Cursor $cursor): int
57
    {
58 423
        $match = $cursor->match('/^\[(?:[^\\\\\[\]]|\\\\.){0,1000}\]/');
59 423
        if ($match === null) {
60 228
            return 0;
61
        }
62
63 396
        $length = \mb_strlen($match, 'utf-8');
64
65 396
        if ($length > 1001) {
66
            return 0;
67
        }
68
69 396
        return $length;
70
    }
71
72
    /**
73
     * Attempt to parse link title (sans quotes)
74
     *
75
     * @param Cursor $cursor
76
     *
77
     * @return null|string The string, or null if no match
78
     */
79 183
    public static function parseLinkTitle(Cursor $cursor): ?string
80
    {
81 183
        if ($title = $cursor->match('/' . RegexHelper::PARTIAL_LINK_TITLE . '/')) {
82
            // Chop off quotes from title and unescape
83 138
            return RegexHelper::unescape(\substr($title, 1, -1));
84
        }
85
86 45
        return null;
87
    }
88
89
    /**
90
     * @param Cursor $cursor
91
     *
92
     * @return string|null
93
     */
94 381
    private static function manuallyParseLinkDestination(Cursor $cursor): ?string
95
    {
96 381
        $oldPosition = $cursor->getPosition();
97 381
        $oldState = $cursor->saveState();
98
99 381
        $openParens = 0;
100 381
        while (($c = $cursor->getCharacter()) !== null) {
101 378
            if ($c === '\\' && RegexHelper::isEscapable($cursor->peek())) {
102 18
                $cursor->advanceBy(2);
103 378
            } elseif ($c === '(') {
104 12
                $cursor->advance();
105 12
                $openParens++;
106 378
            } elseif ($c === ')') {
107 114
                if ($openParens < 1) {
108 111
                    break;
109
                }
110
111 12
                $cursor->advance();
112 12
                $openParens--;
113 375
            } elseif (\preg_match(RegexHelper::REGEX_WHITESPACE_CHAR, $c)) {
114 177
                break;
115
            } else {
116 375
                $cursor->advance();
117
            }
118
        }
119
120 381
        if ($cursor->getPosition() === $oldPosition && $c !== ')') {
121 3
            return null;
122
        }
123
124 378
        $newPos = $cursor->getPosition();
125 378
        $cursor->restoreState($oldState);
126
127 378
        $cursor->advanceBy($newPos - $cursor->getPosition());
128
129 378
        return $cursor->getPreviousText();
130
    }
131
}
132