Passed
Push — add/6 ( 1ceb7c...3ff08f )
by
unknown
09:22 queued 04:37
created

Number   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 299
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 126
dl 0
loc 299
rs 6.4799
c 0
b 0
f 0
wmc 54

12 Methods

Rating   Name   Duplication   Size   Complexity  
B offsetExists() 0 19 7
A __toString() 0 3 1
B output() 0 31 8
A unitless() 0 3 1
B offsetGet() 0 20 7
A normalizeUnits() 0 14 4
A __construct() 0 8 3
A unitStr() 0 18 5
A offsetSet() 0 12 6
A offsetUnset() 0 12 6
A normalize() 0 8 1
A coerce() 0 16 5

How to fix   Complexity   

Complex Class

Complex classes like Number 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 Number, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * SCSSPHP
4
 *
5
 * @copyright 2012-2018 Leaf Corcoran
6
 *
7
 * @license http://opensource.org/licenses/MIT MIT
8
 *
9
 * @link http://leafo.github.io/scssphp
10
 */
11
12
namespace Leafo\ScssPhp\Node;
13
14
use Leafo\ScssPhp\Compiler;
15
use Leafo\ScssPhp\Node;
16
use Leafo\ScssPhp\Type;
17
18
/**
19
 * Dimension + optional units
20
 *
21
 * {@internal
22
 *     This is a work-in-progress.
23
 *
24
 *     The \ArrayAccess interface is temporary until the migration is complete.
25
 * }}
26
 *
27
 * @author Anthon Pang <[email protected]>
28
 */
29
class Number extends Node implements \ArrayAccess
30
{
31
    /**
32
     * @var integer
33
     */
34
    static public $precision = 10;
35
36
    /**
37
     * @see http://www.w3.org/TR/2012/WD-css3-values-20120308/
38
     *
39
     * @var array
40
     */
41
    static protected $unitTable = [
42
        'in' => [
43
            'in' => 1,
44
            'pc' => 6,
45
            'pt' => 72,
46
            'px' => 96,
47
            'cm' => 2.54,
48
            'mm' => 25.4,
49
            'q'  => 101.6,
50
        ],
51
        'turn' => [
52
            'deg'  => 360,
53
            'grad' => 400,
54
            'rad'  => 6.28318530717958647692528676, // 2 * M_PI
55
            'turn' => 1,
56
        ],
57
        's' => [
58
            's'  => 1,
59
            'ms' => 1000,
60
        ],
61
        'Hz' => [
62
            'Hz'  => 1,
63
            'kHz' => 0.001,
64
        ],
65
        'dpi' => [
66
            'dpi'  => 1,
67
            'dpcm' => 2.54,
68
            'dppx' => 96,
69
        ],
70
    ];
71
72
    /**
73
     * @var integer|float
74
     */
75
    public $dimension;
76
77
    /**
78
     * @var array
79
     */
80
    public $units;
81
82
    /**
83
     * Initialize number
84
     *
85
     * @param mixed $dimension
86
     * @param mixed $initialUnit
87
     */
88
    public function __construct($dimension, $initialUnit)
89
    {
90
        $this->type      = Type::T_NUMBER;
91
        $this->dimension = $dimension;
92
        $this->units     = is_array($initialUnit)
93
            ? $initialUnit
94
            : ($initialUnit ? [$initialUnit => 1]
95
                            : []);
96
    }
97
98
    /**
99
     * Coerce number to target units
100
     *
101
     * @param array $units
102
     *
103
     * @return \Leafo\ScssPhp\Node\Number
104
     */
105
    public function coerce($units)
106
    {
107
        if ($this->unitless()) {
108
            return new Number($this->dimension, $units);
109
        }
110
111
        $dimension = $this->dimension;
112
113
        foreach (static::$unitTable['in'] as $unit => $conv) {
114
            $from       = isset($this->units[$unit]) ? $this->units[$unit] : 0;
115
            $to         = isset($units[$unit]) ? $units[$unit] : 0;
116
            $factor     = pow($conv, $from - $to);
117
            $dimension /= $factor;
118
        }
119
120
        return new Number($dimension, $units);
121
    }
122
123
    /**
124
     * Normalize number
125
     *
126
     * @return \Leafo\ScssPhp\Node\Number
127
     */
128
    public function normalize()
129
    {
130
        $dimension = $this->dimension;
131
        $units     = [];
132
133
        $this->normalizeUnits($dimension, $units, 'in');
134
135
        return new Number($dimension, $units);
136
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141
    public function offsetExists($offset)
142
    {
143
        if ($offset === -3) {
144
            return $this->sourceColumn !== null;
145
        }
146
147
        if ($offset === -2) {
148
            return $this->sourceLine !== null;
149
        }
150
151
        if ($offset === -1
152
            || $offset === 0
153
            || $offset === 1
154
            || $offset === 2
155
        ) {
156
            return true;
157
        }
158
159
        return false;
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function offsetGet($offset)
166
    {
167
        switch ($offset) {
168
            case -3:
169
                return $this->sourceColumn;
170
171
            case -2:
172
                return $this->sourceLine;
173
174
            case -1:
175
                return $this->sourceIndex;
176
177
            case 0:
178
                return $this->type;
179
180
            case 1:
181
                return $this->dimension;
182
183
            case 2:
184
                return $this->units;
185
        }
186
    }
187
188
    /**
189
     * {@inheritdoc}
190
     */
191
    public function offsetSet($offset, $value)
192
    {
193
        if ($offset === 1) {
194
            $this->dimension = $value;
195
        } elseif ($offset === 2) {
196
            $this->units = $value;
197
        } elseif ($offset == -1) {
198
            $this->sourceIndex = $value;
199
        } elseif ($offset == -2) {
200
            $this->sourceLine = $value;
201
        } elseif ($offset == -3) {
202
            $this->sourceColumn = $value;
203
        }
204
    }
205
206
    /**
207
     * {@inheritdoc}
208
     */
209
    public function offsetUnset($offset)
210
    {
211
        if ($offset === 1) {
212
            $this->dimension = null;
213
        } elseif ($offset === 2) {
214
            $this->units = null;
215
        } elseif ($offset === -1) {
216
            $this->sourceIndex = null;
217
        } elseif ($offset === -2) {
218
            $this->sourceLine = null;
219
        } elseif ($offset === -3) {
220
            $this->sourceColumn = null;
221
        }
222
    }
223
224
    /**
225
     * Returns true if the number is unitless
226
     *
227
     * @return boolean
228
     */
229
    public function unitless()
230
    {
231
        return ! array_sum($this->units);
232
    }
233
234
    /**
235
     * Returns unit(s) as the product of numerator units divided by the product of denominator units
236
     *
237
     * @return string
238
     */
239
    public function unitStr()
240
    {
241
        $numerators   = [];
242
        $denominators = [];
243
244
        foreach ($this->units as $unit => $unitSize) {
245
            if ($unitSize > 0) {
246
                $numerators = array_pad($numerators, count($numerators) + $unitSize, $unit);
247
                continue;
248
            }
249
250
            if ($unitSize < 0) {
251
                $denominators = array_pad($denominators, count($denominators) + $unitSize, $unit);
252
                continue;
253
            }
254
        }
255
256
        return implode('*', $numerators) . (count($denominators) ? '/' . implode('*', $denominators) : '');
257
    }
258
259
    /**
260
     * Output number
261
     *
262
     * @param \Leafo\ScssPhp\Compiler $compiler
263
     *
264
     * @return string
265
     */
266
    public function output(Compiler $compiler = null)
267
    {
268
        $dimension = round($this->dimension, static::$precision);
269
270
        $units = array_filter($this->units, function ($unitSize) {
271
            return $unitSize;
272
        });
273
274
        if (count($units) > 1 && array_sum($units) === 0) {
275
            $dimension = $this->dimension;
276
            $units     = [];
277
278
            $this->normalizeUnits($dimension, $units, 'in');
279
280
            $dimension = round($dimension, static::$precision);
281
            $units     = array_filter($units, function ($unitSize) {
282
                return $unitSize;
283
            });
284
        }
285
286
        $unitSize = array_sum($units);
287
288
        if ($compiler && ($unitSize > 1 || $unitSize < 0 || count($units) > 1)) {
289
            $compiler->throwError((string) $dimension . $this->unitStr() . " isn't a valid CSS value.");
290
        }
291
292
        reset($units);
293
        $unit = key($units);
294
        $dimension = number_format($dimension, static::$precision, '.', '');
295
296
        return (static::$precision ? rtrim(rtrim($dimension, '0'), '.') : $dimension) . $unit;
297
    }
298
299
    /**
300
     * {@inheritdoc}
301
     */
302
    public function __toString()
303
    {
304
        return $this->output();
305
    }
306
307
    /**
308
     * Normalize units
309
     *
310
     * @param integer|float $dimension
311
     * @param array         $units
312
     * @param string        $baseUnit
313
     */
314
    private function normalizeUnits(&$dimension, &$units, $baseUnit = 'in')
315
    {
316
        $dimension = $this->dimension;
317
        $units     = [];
318
319
        foreach ($this->units as $unit => $exp) {
320
            if (isset(static::$unitTable[$baseUnit][$unit])) {
321
                $factor = pow(static::$unitTable[$baseUnit][$unit], $exp);
322
323
                $unit = $baseUnit;
324
                $dimension /= $factor;
325
            }
326
327
            $units[$unit] = $exp + (isset($units[$unit]) ? $units[$unit] : 0);
328
        }
329
    }
330
}
331