ListTrait   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 188
Duplicated Lines 14.89 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 98.63%

Importance

Changes 0
Metric Value
wmc 36
lcom 1
cbo 0
dl 28
loc 188
ccs 72
cts 73
cp 0.9863
rs 9.52
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A identifyOl() 0 4 1
A identifyUl() 0 4 1
A isParentItem() 6 14 6
A isSiblingItem() 0 10 6
A consumeOl() 11 11 1
A consumeUl() 11 11 1
D consumeList() 0 66 18
A renderList() 0 9 2
parseInline() 0 1 ?
renderAbsy() 0 1 ?
identifyHeadline() 0 1 ?
identifyHr() 0 1 ?
identifyTable() 0 1 ?
identifyCode() 0 1 ?

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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