ListTrait::consumeList()   D
last analyzed

Complexity

Conditions 18
Paths 44

Size

Total Lines 66

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 42
CRAP Score 18

Importance

Changes 0
Metric Value
dl 0
loc 66
ccs 42
cts 42
cp 1
rs 4.8666
c 0
b 0
f 0
cc 18
nc 44
nop 4
crap 18

How to fix   Long Method    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
 * @copyright Copyright (c) 2015 Nobuo Kihara
4
 * @license https://github.com/softark/creole/blob/master/LICENSE
5
 * @link https://github.com/softark/creole#readme
6
 */
7
8
namespace softark\creole\block;
9
10
/**
11
 * Adds the list blocks
12
 */
13
trait ListTrait
14
{
15
    /**
16
     * @var int the current depth of the nested lists
17
     */
18
    private $_listDepth = 1;
19
20
    /**
21
     * @var array the types of the nested lists
22
     */
23
    private $_nestedListTypes = [];
24
25
    /**
26
     * Identify a line as the beginning of an ordered list.
27
     * It should start with '#', with leading white spaces permitted.
28
     */
29 23
    protected function identifyOl($line)
30
    {
31 23
        return preg_match('/^\s*#{' . $this->_listDepth . '}[^#]+/', $line);
32
    }
33
34
    /**
35
     * Identify a line as the beginning of an unordered list.
36
     * It should start with '*', with leading white spaces permitted.
37
     */
38 21
    protected function identifyUl($line)
39
    {
40 21
        return preg_match('/^\s*\*{' . $this->_listDepth . '}[^\*]+/', $line);
41
    }
42
43
    /**
44
     * check if a line is an item that belongs to a parent list
45
     * @param $line
46
     * @return bool true if the line is an item of a parent list
47
     */
48 5
    protected function isParentItem($line)
49
    {
50 5
        if ($this->_listDepth === 1 || ($marker = $line[0]) !== '*' && $marker !== '#') {
51 5
            return false;
52
        }
53 3
        $depthMax = $this->_listDepth - 1;
54 3 View Code Duplication
        if (preg_match('/^(#{1,' . $depthMax . '})[^#]+/', $line, $matches)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
55
            return $this->_nestedListTypes[strlen($matches[1])] === 'ol';
56
        }
57 3 View Code Duplication
        if (preg_match('/^(\*{1,' . $depthMax . '})[^\*]+/', $line, $matches)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
58 2
            return $this->_nestedListTypes[strlen($matches[1])] === 'ul';
59
        }
60 3
        return false;
61
    }
62
63
    /**
64
     * check if a line is an item that belongs to a sibling list
65
     * @param $line
66
     * @return bool true if the line is an item of a sibling list
67
     */
68 5
    protected function isSiblingItem($line)
69
    {
70 5
        $siblingMarker = $this->_nestedListTypes[$this->_listDepth] == 'ol' ? '*' : '#';
71 5
        if ($line[0] !== $siblingMarker) {
72 5
            return false;
73
        }
74
        return
75 1
            ($siblingMarker === '#' && preg_match('/^#{' . $this->_listDepth . '}[^#]+/', $line)) ||
76 1
            ($siblingMarker === '*' && preg_match('/^\*{' . $this->_listDepth . '}[^\*]+/', $line));
77
    }
78
79
    /**
80
     * Consume lines for an ordered list
81
     */
82 4 View Code Duplication
    protected function consumeOl($lines, $current)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
83
    {
84
        // consume until newline
85
86
        $block = [
87 4
            'list',
88
            'list' => 'ol',
89
            'items' => [],
90
        ];
91 4
        return $this->consumeList($lines, $current, $block, 'ol');
92
    }
93
94
    /**
95
     * Consume lines for an unordered list
96
     */
97 5 View Code Duplication
    protected function consumeUl($lines, $current)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
98
    {
99
        // consume until newline
100
101
        $block = [
102 5
            'list',
103
            'list' => 'ul',
104
            'items' => [],
105
        ];
106 5
        return $this->consumeList($lines, $current, $block, 'ul');
107
    }
108
109 5
    private function consumeList($lines, $current, $block, $type)
110
    {
111 5
        $this->_nestedListTypes[$this->_listDepth] = $type;
112 5
        $item = 0;
113 5
        $pattern = $type === 'ul' ? '/^\*{' . $this->_listDepth . '}([^\*]+.*|)$/' : '/^#{' . $this->_listDepth . '}([^#]+.*|)$/';
114 5
        for ($i = $current, $count = count($lines); $i < $count; $i++) {
115 5
            $line = ltrim($lines[$i]);
116
            // A list ends with a blank new line, other block elements, a parent item, or a sibling list.
117 5
            if ($line === '' ||
118 5
                $this->identifyHeadline($line) ||
119 5
                $this->identifyHr($line) ||
120 5
                $this->identifyTable($line) ||
121 5
                $this->identifyCode($line) ||
122 5
                $this->isParentItem($line) ||
123 5
                $this->isSiblingItem($line)
124
            ) {
125
                // list ended
126 5
                $i--;
127 5
                break;
128
            }
129 5
            if (preg_match($pattern, $line)) {
130
                // match list marker on the beginning of the line ... the next item begins
131 5
                $line = ltrim(substr($line, $this->_listDepth));
132 5
                $block['items'][++$item][] = $line;
133
            } else {
134
                // child list?
135 4
                $this->_listDepth++;
136 4
                if ($this->identifyOl($line)) {
137 3
                    list($childBlock, $i) = $this->consumeOl($lines, $i);
138 3
                    $block['items'][$item][] = $childBlock;
139 4
                } elseif ($this->identifyUl($line)) {
140 3
                    list($childBlock, $i) = $this->consumeUl($lines, $i);
141 3
                    $block['items'][$item][] = $childBlock;
142
                } else {
143
                    // the continuing content of the current item
144 3
                    $line = ltrim($line);
145 3
                    $block['items'][$item][] = $line;
146
                }
147 4
                $this->_listDepth--;
148
            }
149
        }
150
151 5
        foreach ($block['items'] as $itemId => $itemLines) {
152 5
            $content = [];
153 5
            $texts = [];
154 5
            foreach ($itemLines as $line) {
155 5
                if (!isset($line['list'])) {
156 5
                    $texts[] = $line;
157
                } else {
158
                    // child list
159 3
                    if (!empty($texts)) {
160
                        // text before child list
161 3
                        $content = array_merge($content, $this->parseInline(implode("\n", $texts)));
162 3
                        $texts = [];
163
                    }
164 5
                    $content[] = $line;
165
                }
166
            }
167 5
            if (!empty($texts)) {
168 5
                $content = array_merge($content, $this->parseInline(implode("\n", $texts)));
169
            }
170 5
            $block['items'][$itemId] = $content;
171
        }
172
173 5
        return [$block, $i];
174
    }
175
176
    /**
177
     * Renders a list
178
     */
179 5
    protected function renderList($block)
180
    {
181 5
        $type = $block['list'];
182 5
        $output = "<$type>\n";
183 5
        foreach ($block['items'] as $item => $itemLines) {
184 5
            $output .= '<li>' . $this->renderAbsy($itemLines) . "</li>\n";
185
        }
186 5
        return $output . "</$type>\n";
187
    }
188
189
    abstract protected function parseInline($text);
190
191
    abstract protected function renderAbsy($absy);
192
193
    abstract protected function identifyHeadline($line);
194
195
    abstract protected function identifyHr($line);
196
197
    abstract protected function identifyTable($line);
198
199
    abstract protected function identifyCode($line);
200
}
201