GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

DelimiterStack   B
last analyzed

↳ Parent: Project

Coupling/Cohesion

Components 1
Dependencies 1

Complexity

Total Complexity 42

Size/Duplication

Total Lines 193
Duplicated Lines 0 %

Test Coverage

Coverage 84.21%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 42
c 1
b 0
f 0
lcom 1
cbo 1
dl 0
loc 193
ccs 80
cts 95
cp 0.8421
rs 8.295

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getTop() 0 4 1
A push() 0 10 2
A findEarliest() 0 9 3
A removeDelimiter() 0 13 3
A removeAll() 0 6 3
A removeEarlierMatches() 0 11 3
A searchByCharacter() 0 16 4
B findFirstMatchingOpener() 0 13 6
C iterateByCharacters() 0 39 8
B findMatchingOpener() 0 14 9

How to fix   Complexity   

Complex Class

Complex classes like DelimiterStack often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DelimiterStack, and based on these observations, apply Extract Interface, too.

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\Delimiter;
16
17
class DelimiterStack
18
{
19
    /**
20
     * @var Delimiter|null
21
     */
22
    protected $top;
23
24
    public function getTop()
25
    {
26
        return $this->top;
27
    }
28
29 855
    public function push(Delimiter $newDelimiter)
30
    {
31 855
        $newDelimiter->setPrevious($this->top);
32
33 855
        if ($this->top !== null) {
34 498
            $this->top->setNext($newDelimiter);
35 498
        }
36
37 855
        $this->top = $newDelimiter;
38 855
    }
39
40
    /**
41
     * @param Delimiter|null $stackBottom
42
     *
43
     * @return Delimiter|null
44
     */
45 1638
    public function findEarliest(Delimiter $stackBottom = null)
46
    {
47 1638
        $delimiter = $this->top;
48 1638
        while ($delimiter !== null && $delimiter->getPrevious() !== $stackBottom) {
49 456
            $delimiter = $delimiter->getPrevious();
50 456
        }
51
52 1638
        return $delimiter;
53
    }
54
55
    /**
56
     * @param Delimiter $delimiter
57
     */
58 855
    public function removeDelimiter(Delimiter $delimiter)
59
    {
60 855
        if ($delimiter->getPrevious() !== null) {
61 339
            $delimiter->getPrevious()->setNext($delimiter->getNext());
62 339
        }
63
64 855
        if ($delimiter->getNext() === null) {
65
            // top of stack
66 855
            $this->top = $delimiter->getPrevious();
67 855
        } else {
68 324
            $delimiter->getNext()->setPrevious($delimiter->getPrevious());
69
        }
70 855
    }
71
72
    /**
73
     * @param Delimiter|null $stackBottom
74
     */
75 1638
    public function removeAll(Delimiter $stackBottom = null)
76
    {
77 1638
        while ($this->top && $this->top !== $stackBottom) {
78 522
            $this->removeDelimiter($this->top);
79 522
        }
80 1638
    }
81
82
    /**
83
     * @param string $character
84
     */
85 279
    public function removeEarlierMatches($character)
86
    {
87 279
        $opener = $this->top;
88 279
        while ($opener !== null) {
89 54
            if ($opener->getChar() === $character) {
90 24
                $opener->setActive(false);
91 24
            }
92
93 54
            $opener = $opener->getPrevious();
94 54
        }
95 279
    }
96
97
    /**
98
     * @param string|string[] $characters
99
     *
100
     * @return Delimiter|null
101
     */
102 408
    public function searchByCharacter($characters)
103
    {
104 408
        if (!is_array($characters)) {
105
            $characters = [$characters];
106
        }
107
108 408
        $opener = $this->top;
109 408
        while ($opener !== null) {
110 402
            if (in_array($opener->getChar(), $characters)) {
111 399
                break;
112
            }
113 72
            $opener = $opener->getPrevious();
114 72
        }
115
116 408
        return $opener;
117
    }
118
119
    /**
120
     * @param string|string[] $characters
121
     * @param callable        $callback
122
     * @param Delimiter       $stackBottom
123
     */
124 1638
    public function iterateByCharacters($characters, $callback, Delimiter $stackBottom = null)
125
    {
126 1638
        if (!is_array($characters)) {
127
            $characters = [$characters];
128
        }
129
130 1638
        $openersBottom = array_fill_keys($characters, $stackBottom);
131
132
        // Find first closer above stackBottom
133 1638
        $closer = $this->findEarliest($stackBottom);
134
135 1638
        while ($closer !== null) {
136 795
            $closerChar = $closer->getChar();
137
138 795
            if (!$closer->canClose() || !in_array($closerChar, $characters)) {
139 759
                $closer = $closer->getNext();
140 759
                continue;
141
            }
142
143 399
            $oddMatch = false;
144 399
            $opener = $this->findMatchingOpener($closer, $openersBottom, $stackBottom, $oddMatch);
145 399
            if ($opener) {
146 327
                $closer = $callback($opener, $closer, $this);
147 399
            } elseif ($oddMatch) {
148 15
                $closer = $closer->getNext();
149 15
            } else {
150 114
                $oldCloser = $closer;
151 114
                $closer = $closer->getNext();
152
                // Set lower bound for future searches for openers:
153 114
                $openersBottom[$closerChar] = $oldCloser->getPrevious();
154 114
                if (!$oldCloser->canOpen()) {
155
                    // We can remove a closer that can't be an opener,
156
                    // once we've seen there's no matching opener:
157 87
                    $this->removeDelimiter($oldCloser);
158 87
                }
159 114
                continue;
160
            }
161 327
        }
162 1638
    }
163
164
    /**
165
     * @param Delimiter      $closer
166
     * @param array          $openersBottom
167
     * @param Delimiter|null $stackBottom
168
     * @param bool           $oddMatch
169
     *
170
     * @return Delimiter|null
171
     */
172 399
    protected function findMatchingOpener(Delimiter $closer, $openersBottom, Delimiter $stackBottom = null, &$oddMatch = false)
173
    {
174 399
        $closerChar = $closer->getChar();
175 399
        $opener = $closer->getPrevious();
176
177 399
        while ($opener !== null && $opener !== $stackBottom && $opener !== $openersBottom[$closerChar]) {
178 375
            $oddMatch = ($closer->canOpen() || $opener->canClose()) && ($opener->getNumDelims() + $closer->getNumDelims()) % 3 === 0;
179 375
            if ($opener->getChar() === $closerChar && $opener->canOpen() && !$oddMatch) {
180 327
                return $opener;
181
            }
182
183 81
            $opener = $opener->getPrevious();
184 81
        }
185 129
    }
186
187
    /**
188
     * @param Delimiter      $closer
189
     * @param array          $openersBottom
190
     * @param Delimiter|null $stackBottom
191
     *
192
     * @return Delimiter|null
193
     *
194
     * @deprecated Use findMatchingOpener() instead.  This method will be removed in the next major release.
195
     */
196
    protected function findFirstMatchingOpener(Delimiter $closer, $openersBottom, Delimiter $stackBottom = null)
197
    {
198
        $closerChar = $closer->getChar();
199
        $opener = $closer->getPrevious();
200
201
        while ($opener !== null && $opener !== $stackBottom && $opener !== $openersBottom[$closerChar]) {
202
            if ($opener->getChar() === $closerChar && $opener->canOpen()) {
203
                return $opener;
204
            }
205
206
            $opener = $opener->getPrevious();
207
        }
208
    }
209
}
210