Passed
Push — master ( d4a329...711fef )
by Jeroen De
03:36
created

Nested::block()   F

Complexity

Conditions 42
Paths > 20000

Size

Total Lines 135
Code Lines 79

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 42
eloc 79
c 1
b 0
f 0
nc 324030
nop 1
dl 0
loc 135
rs 0

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
/**
4
 * SCSSPHP
5
 *
6
 * @copyright 2012-2020 Leaf Corcoran
7
 *
8
 * @license http://opensource.org/licenses/MIT MIT
9
 *
10
 * @link http://scssphp.github.io/scssphp
11
 */
12
13
namespace ScssPhp\ScssPhp\Formatter;
14
15
use ScssPhp\ScssPhp\Formatter;
16
use ScssPhp\ScssPhp\Formatter\OutputBlock;
17
use ScssPhp\ScssPhp\Type;
18
19
/**
20
 * Nested formatter
21
 *
22
 * @author Leaf Corcoran <[email protected]>
23
 */
24
class Nested extends Formatter
25
{
26
    /**
27
     * @var integer
28
     */
29
    private $depth;
30
31
    /**
32
     * {@inheritdoc}
33
     */
34
    public function __construct()
35
    {
36
        $this->indentLevel = 0;
37
        $this->indentChar = '  ';
38
        $this->break = "\n";
39
        $this->open = ' {';
40
        $this->close = ' }';
41
        $this->tagSeparator = ', ';
42
        $this->assignSeparator = ': ';
43
        $this->keepSemicolons = true;
44
    }
45
46
    /**
47
     * {@inheritdoc}
48
     */
49
    protected function indentStr()
50
    {
51
        $n = $this->depth - 1;
52
53
        return str_repeat($this->indentChar, max($this->indentLevel + $n, 0));
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59
    protected function blockLines(OutputBlock $block)
60
    {
61
        $inner = $this->indentStr();
62
        $glue  = $this->break . $inner;
63
64
        foreach ($block->lines as $index => $line) {
65
            if (substr($line, 0, 2) === '/*') {
66
                $block->lines[$index] = preg_replace('/\r\n?|\n|\f/', $this->break, $line);
67
            }
68
        }
69
70
        $this->write($inner . implode($glue, $block->lines));
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76
    protected function block(OutputBlock $block)
77
    {
78
        static $depths;
79
        static $downLevel;
80
        static $closeBlock;
81
        static $previousEmpty;
82
        static $previousHasSelector;
83
84
        if ($block->type === 'root') {
85
            $depths = [ 0 ];
86
            $downLevel = '';
87
            $closeBlock = '';
88
            $this->depth = 0;
89
            $previousEmpty = false;
90
            $previousHasSelector = false;
91
        }
92
93
        $isMediaOrDirective = \in_array($block->type, [Type::T_DIRECTIVE, Type::T_MEDIA]);
94
        $isSupport = ($block->type === Type::T_DIRECTIVE
95
            && $block->selectors && strpos(implode('', $block->selectors), '@supports') !== false);
0 ignored issues
show
Bug Best Practice introduced by
The expression $block->selectors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
96
97
        while ($block->depth < end($depths) || ($block->depth == 1 && end($depths) == 1)) {
98
            array_pop($depths);
99
            $this->depth--;
100
101
            if (
102
                ! $this->depth && ($block->depth <= 1 || (! $this->indentLevel && $block->type === Type::T_COMMENT)) &&
103
                (($block->selectors && ! $isMediaOrDirective) || $previousHasSelector)
0 ignored issues
show
Bug Best Practice introduced by
The expression $block->selectors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
104
            ) {
105
                $downLevel = $this->break;
106
            }
107
108
            if (empty($block->lines) && empty($block->children)) {
109
                $previousEmpty = true;
110
            }
111
        }
112
113
        if (empty($block->lines) && empty($block->children)) {
114
            return;
115
        }
116
117
        $this->currentBlock = $block;
118
119
        if (! empty($block->lines) || (! empty($block->children) && ($this->depth < 1 || $isSupport))) {
120
            if ($block->depth > end($depths)) {
121
                if (! $previousEmpty || $this->depth < 1) {
122
                    $this->depth++;
123
124
                    $depths[] = $block->depth;
125
                } else {
126
                    // keep the current depth unchanged but take the block depth as a new reference for following blocks
127
                    array_pop($depths);
128
129
                    $depths[] = $block->depth;
130
                }
131
            }
132
        }
133
134
        $previousEmpty = ($block->type === Type::T_COMMENT);
135
        $previousHasSelector = false;
136
137
        if (! empty($block->selectors)) {
138
            if ($closeBlock) {
139
                $this->write($closeBlock);
140
                $closeBlock = '';
141
            }
142
143
            if ($downLevel) {
144
                $this->write($downLevel);
145
                $downLevel = '';
146
            }
147
148
            $this->blockSelectors($block);
149
150
            $this->indentLevel++;
151
        }
152
153
        if (! empty($block->lines)) {
154
            if ($closeBlock) {
155
                $this->write($closeBlock);
156
                $closeBlock = '';
157
            }
158
159
            if ($downLevel) {
160
                $this->write($downLevel);
161
                $downLevel = '';
162
            }
163
164
            $this->blockLines($block);
165
166
            $closeBlock = $this->break;
167
        }
168
169
        if (! empty($block->children)) {
170
            if ($this->depth > 0 && ($isMediaOrDirective || ! $this->hasFlatChild($block))) {
171
                array_pop($depths);
172
173
                $this->depth--;
174
                $this->blockChildren($block);
175
                $this->depth++;
176
177
                $depths[] = $block->depth;
178
            } else {
179
                $this->blockChildren($block);
180
            }
181
        }
182
183
        // reclear to not be spoiled by children if T_DIRECTIVE
184
        if ($block->type === Type::T_DIRECTIVE) {
185
            $previousHasSelector = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $previousHasSelector is dead and can be removed.
Loading history...
186
        }
187
188
        if (! empty($block->selectors)) {
189
            $this->indentLevel--;
190
191
            if (! $this->keepSemicolons) {
192
                $this->strippedSemicolon = '';
193
            }
194
195
            $this->write($this->close);
196
197
            $closeBlock = $this->break;
198
199
            if ($this->depth > 1 && ! empty($block->children)) {
200
                array_pop($depths);
201
                $this->depth--;
202
            }
203
204
            if (! $isMediaOrDirective) {
205
                $previousHasSelector = true;
206
            }
207
        }
208
209
        if ($block->type === 'root') {
210
            $this->write($this->break);
211
        }
212
    }
213
214
    /**
215
     * Block has flat child
216
     *
217
     * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
218
     *
219
     * @return boolean
220
     */
221
    private function hasFlatChild($block)
222
    {
223
        foreach ($block->children as $child) {
224
            if (empty($child->selectors)) {
225
                return true;
226
            }
227
        }
228
229
        return false;
230
    }
231
}
232