BlockElementParser   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 226
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 117
c 1
b 0
f 1
dl 0
loc 226
rs 8.48
wmc 49

11 Methods

Rating   Name   Duplication   Size   Complexity  
A isBlock() 0 3 5
B parseBlockElements() 0 18 8
A setext() 0 8 2
A atx() 0 9 2
A code() 0 19 6
A listt() 0 21 5
A table() 0 21 4
A rule() 0 5 1
A codeInternal() 0 16 5
A tableInternal() 0 11 3
B listInternal() 0 29 8

How to fix   Complexity   

Complex Class

Complex classes like BlockElementParser 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.

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 BlockElementParser, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the HTMLUP package.
5
 *
6
 * (c) Jitendra Adhikari <[email protected]>
7
 *     <https://github.com/adhocore>
8
 *
9
 * Licensed under MIT license.
10
 */
11
12
namespace Ahc;
13
14
abstract class BlockElementParser
15
{
16
    use HtmlHelper;
17
18
    const RE_MD_QUOTE  = '~^\s*(>+)\s+~';
19
    const RE_RAW       = '/^<\/?\w.*?\/?>/';
20
    const RE_MD_SETEXT = '~^\s*(={3,}|-{3,})\s*$~';
21
    const RE_MD_CODE   = '/^```\s*([\w-]+)?/';
22
    const RE_MD_RULE   = '~^(_{3,}|\*{3,}|\-{3,})$~';
23
    const RE_MD_TCOL   = '~(\|\s*\:)?\s*\-{3,}\s*(\:\s*\|)?~';
24
    const RE_MD_OL     = '/^\d+\. /';
25
26
    protected $lines       = [];
27
    protected $stackList   = [];
28
    protected $stackBlock  = [];
29
    protected $stackTable  = [];
30
31
    protected $pointer     = -1;
32
    protected $listLevel   = 0;
33
    protected $quoteLevel  = 0;
34
    protected $indent      = 0;
35
    protected $nextIndent  = 0;
36
    protected $indentLen   = 4;
37
38
    protected $indentStr       = '    ';
39
    protected $line            = '';
40
    protected $trimmedLine     = '';
41
    protected $prevLine        = '';
42
    protected $trimmedPrevLine = '';
43
    protected $nextLine        = '';
44
    protected $trimmedNextLine = '';
45
    protected $markup          = '';
46
47
    protected $inList  = \false;
48
    protected $inQuote = \false;
49
    protected $inPara  = \false;
50
    protected $inHtml  = \false;
51
    protected $inTable = \false;
52
53
    protected function parseBlockElements()
54
    {
55
        while (isset($this->lines[++$this->pointer])) {
56
            $this->init();
0 ignored issues
show
Bug introduced by
The method init() does not exist on Ahc\BlockElementParser. Since it exists in all sub-types, consider adding an abstract or default implementation to Ahc\BlockElementParser. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

56
            $this->/** @scrutinizer ignore-call */ 
57
                   init();
Loading history...
57
58
            if ($this->flush() || $this->raw()) {
0 ignored issues
show
Bug introduced by
The method flush() does not exist on Ahc\BlockElementParser. Since it exists in all sub-types, consider adding an abstract or default implementation to Ahc\BlockElementParser. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

58
            if ($this->/** @scrutinizer ignore-call */ flush() || $this->raw()) {
Loading history...
Bug introduced by
The method raw() does not exist on Ahc\BlockElementParser. Since it exists in all sub-types, consider adding an abstract or default implementation to Ahc\BlockElementParser. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

58
            if ($this->flush() || $this->/** @scrutinizer ignore-call */ raw()) {
Loading history...
59
                continue;
60
            }
61
62
            $this->quote();
0 ignored issues
show
Bug introduced by
The method quote() does not exist on Ahc\BlockElementParser. Since it exists in all sub-types, consider adding an abstract or default implementation to Ahc\BlockElementParser. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

62
            $this->/** @scrutinizer ignore-call */ 
63
                   quote();
Loading history...
63
64
            if (($block = $this->isBlock()) || $this->inList) {
65
                $this->markup .= $block ? '' : $this->trimmedLine;
66
67
                continue;
68
            }
69
70
            $this->table() || $this->paragraph();
0 ignored issues
show
Bug introduced by
The method paragraph() does not exist on Ahc\BlockElementParser. Since it exists in all sub-types, consider adding an abstract or default implementation to Ahc\BlockElementParser. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

70
            $this->table() || $this->/** @scrutinizer ignore-call */ paragraph();
Loading history...
71
        }
72
    }
73
74
    protected function isBlock()
75
    {
76
        return $this->atx() || $this->setext() || $this->code() || $this->rule() || $this->listt();
77
    }
78
79
    protected function atx()
80
    {
81
        if (\substr($this->trimmedLine, 0, 1) === '#') {
82
            $level = \strlen($this->trimmedLine) - \strlen(\ltrim($this->trimmedLine, '#'));
83
            $head  = $this->h($level, $this->trimmedLine);
84
85
            $this->markup .= $head;
86
87
            return (bool) $head;
88
        }
89
    }
90
91
    protected function setext()
92
    {
93
        if (\preg_match(static::RE_MD_SETEXT, $this->nextLine)) {
94
            $this->markup .= $this->h($this->nextLine, $this->trimmedLine);
95
96
            $this->pointer++;
97
98
            return \true;
99
        }
100
    }
101
102
    protected function code()
103
    {
104
        $isShifted = ($this->indent - $this->nextIndent) >= $this->indentLen;
105
        $codeBlock = \preg_match(static::RE_MD_CODE, $this->line, $codeMatch);
106
107
        if ($codeBlock || (!$this->inList && !$this->inQuote && $isShifted)) {
108
            $this->markup .= $this->codeStart($codeMatch);
109
110
            if (!$codeBlock) {
111
                $this->markup .= $this->escape(\substr($this->line, $this->indentLen));
112
            }
113
114
            $this->codeInternal($codeBlock);
115
116
            $this->pointer++;
117
118
            $this->markup .= '</code></pre>';
119
120
            return \true;
121
        }
122
    }
123
124
    private function codeInternal($codeBlock)
125
    {
126
        while (isset($this->lines[$this->pointer + 1])) {
127
            $this->line = $this->escape($this->lines[$this->pointer + 1]);
128
129
            if (($codeBlock && \substr(\ltrim($this->line), 0, 3) !== '```')
130
                || \strpos($this->line, $this->indentStr) === 0
131
            ) {
132
                $this->markup .= $this->codeLine($this->line, $codeBlock, $this->indentLen);
133
134
                $this->pointer++;
135
136
                continue;
137
            }
138
139
            break;
140
        }
141
    }
142
143
    protected function rule()
144
    {
145
        $this->markup .= $hr = $this->hr($this->trimmedPrevLine, $this->trimmedLine);
146
147
        return (bool) $hr;
148
    }
149
150
    protected function listt()
151
    {
152
        $isUl = \in_array(\substr($this->trimmedLine, 0, 2), ['- ', '* ', '+ ']);
153
154
        if ($isUl || \preg_match(static::RE_MD_OL, $this->trimmedLine)) {
155
            $wrapper = $isUl ? 'ul' : 'ol';
156
157
            if (!$this->inList) {
158
                $this->stackList[] = "</$wrapper>";
159
160
                $this->markup .= "\n<$wrapper>\n";
161
                $this->inList  = \true;
162
163
                $this->listLevel++;
164
            }
165
166
            $this->markup .= '<li>' . \ltrim($this->trimmedLine, '+-*0123456789. ');
167
168
            $this->listInternal();
169
170
            return \true;
171
        }
172
    }
173
174
    private function listInternal()
175
    {
176
        $isUl = \in_array(\substr($this->trimmedNextLine, 0, 2), ['- ', '* ', '+ ']);
177
178
        if ($isUl || \preg_match(static::RE_MD_OL, $this->trimmedNextLine)) {
179
            $wrapper = $isUl ? 'ul' : 'ol';
180
            if ($this->nextIndent > $this->indent) {
181
                $this->stackList[] = "</li>\n";
182
                $this->stackList[] = "</$wrapper>";
183
                $this->markup .= "\n<$wrapper>\n";
184
185
                $this->listLevel++;
186
            } else {
187
                $this->markup .= "</li>\n";
188
            }
189
190
            if ($this->nextIndent < $this->indent) {
191
                $shift = \intval(($this->indent - $this->nextIndent) / $this->indentLen);
192
193
                while ($shift--) {
194
                    $this->markup .= \array_pop($this->stackList);
195
196
                    if ($this->listLevel > 2) {
197
                        $this->markup .= \array_pop($this->stackList);
198
                    }
199
                }
200
            }
201
        } else {
202
            $this->markup .= "</li>\n";
203
        }
204
    }
205
206
    protected function table()
207
    {
208
        static $headerCount = 0;
209
210
        if (!$this->inTable) {
211
            $headerCount = \substr_count(\trim($this->trimmedLine, '|'), '|');
212
213
            return $this->tableInternal($headerCount);
214
        }
215
216
        $this->markup .= $this->tableRow($this->trimmedLine, $headerCount);
217
218
        if (empty($this->trimmedNextLine)
219
            || !\substr_count(\trim($this->trimmedNextLine, '|'), '|')
220
        ) {
221
            $headerCount        = 0;
222
            $this->inTable      = \false;
223
            $this->stackTable[] = "</tbody>\n</table>";
224
        }
225
226
        return \true;
227
    }
228
229
    private function tableInternal($headerCount)
230
    {
231
        $columnCount = \preg_match_all(static::RE_MD_TCOL, \trim($this->trimmedNextLine, '|'));
232
233
        if ($headerCount > 0 && $headerCount <= $columnCount) {
234
            $this->pointer++;
235
236
            $this->inTable = \true;
237
            $this->markup .= $this->tableStart($this->trimmedLine);
238
239
            return \true;
240
        }
241
    }
242
}
243