DimensionNode::operate()   C
last analyzed

Complexity

Conditions 12
Paths 8

Size

Total Lines 42
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 42
rs 5.1612
c 0
b 0
f 0
cc 12
eloc 31
nc 8
nop 3

How to fix   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
 * This file is part of the ILess
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace ILess\Node;
11
12
use ILess\Context;
13
use ILess\Exception\CompilerException;
14
use ILess\Math;
15
use ILess\Node;
16
use ILess\Output\OutputInterface;
17
use ILess\Util\UnitConversion;
18
use ILess\Util;
19
use ILess\Visitor\VisitorInterface;
20
21
/**
22
 * Dimension.
23
 */
24
class DimensionNode extends Node implements ComparableInterface, ToColorConvertibleInterface
25
{
26
    /**
27
     * Node type.
28
     *
29
     * @var string
30
     */
31
    protected $type = 'Dimension';
32
33
    /**
34
     * The unit.
35
     *
36
     * @var UnitNode
37
     */
38
    public $unit;
39
40
    /**
41
     * Constructor.
42
     *
43
     * @param string $value
44
     * @param UnitNode|string $unit
45
     */
46
    public function __construct($value, $unit = null)
47
    {
48
        parent::__construct(floatval($value));
49
50
        if (!$unit instanceof UnitNode) {
51
            $unit = $unit ? new UnitNode([$unit]) : new UnitNode();
52
        }
53
54
        $this->unit = $unit;
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60
    public function accept(VisitorInterface $visitor)
61
    {
62
        $this->unit = $visitor->visit($this->unit);
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68
    public function toColor()
69
    {
70
        return new ColorNode([$this->value, $this->value, $this->value]);
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76
    public function generateCSS(Context $context, OutputInterface $output)
77
    {
78
        if ($context->strictUnits && !$this->unit->isSingular()) {
79
            throw new CompilerException(
80
                sprintf(
81
                    'Multiple units in dimension. Correct the units or use the unit function. Bad unit: %s',
82
                    $this->unit->toString()
83
                )
84
            );
85
        }
86
87
        $value = Util::round($context, $this->value);
88
        $strValue = (string) $value;
89
90
        if ($value !== 0 && $value < 0.000001 && $value > -0.000001) {
91
            $strValue = Math::toFixed($value, 20);
92
        }
93
94
        // remove trailing zeros
95
        $strValue = Math::clean($strValue);
96
97
        // Zero values doesn't need a unit
98
        if ($context->compress) {
99
            if ($value == 0 && $this->unit->isLength()) {
100
                $output->add($strValue);
101
102
                return;
103
            }
104
            // Float values doesn't need a leading zero
105
            if ($value > 0 && $value < 1) {
106
                $strValue = substr($strValue, 1);
107
            }
108
        }
109
110
        $output->add($strValue);
111
        // pass to unit
112
        $this->unit->generateCSS($context, $output);
113
    }
114
115
    /**
116
     * Convert the value to string.
117
     *
118
     * @return string
119
     */
120
    public function toString()
121
    {
122
        return $this->toCSS(new Context());
123
    }
124
125
    /**
126
     * Operates with the dimension. In an operation between two dimensions,
127
     * we default to the first Dimension's unit,
128
     * so `1px + 2` will yield `3px`.
129
     *
130
     * @param Context $context
131
     * @param string $op
132
     * @param DimensionNode $other
133
     *
134
     * @return DimensionNode
135
     *
136
     * @throws CompilerException
137
     */
138
    public function operate(Context $context, $op, DimensionNode $other)
139
    {
140
        $value = Math::operate($op, $this->value, $other->value);
0 ignored issues
show
Documentation introduced by
$this->value is of type object<ILess\Node>|string, but the function expects a double.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$other->value is of type object<ILess\Node>|string, but the function expects a double.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
141
        $unit = clone $this->unit;
142
143
        if ($op === '+' || $op === '-') {
144
            if (!count($unit->numerator) && !count($unit->denominator)) {
145
                $unit = clone $other->unit;
146
                if ($this->unit->backupUnit) {
147
                    $unit->backupUnit = $this->unit->backupUnit;
148
                }
149
            } elseif (!count($other->unit->numerator) && !count($other->unit->denominator)) {
150
                // do nothing
151
            } else {
152
                $other = $other->convertTo($this->unit->usedUnits());
153
                if ($context->strictUnits && $other->unit->toString() !== $unit->toString()) {
154
                    throw new CompilerException(
155
                        sprintf(
156
                            'Incompatible units. Change the units or use the unit function. Bad units: \'%s\' and \'%s\'.',
157
                            $unit->toString(),
158
                            $other->unit->toString()
159
                        )
160
                    );
161
                }
162
                $value = Math::operate($op, $this->value, $other->value);
0 ignored issues
show
Documentation introduced by
$this->value is of type object<ILess\Node>|string, but the function expects a double.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$other->value is of type object<ILess\Node>|string, but the function expects a double.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
163
            }
164
        } elseif ($op === '*') {
165
            $unit->numerator = array_merge($unit->numerator, $other->unit->numerator);
166
            $unit->denominator = array_merge($unit->denominator, $other->unit->denominator);
167
            sort($unit->numerator);
168
            sort($unit->denominator);
169
            $unit->cancel();
170
        } elseif ($op === '/') {
171
            $unit->numerator = array_merge($unit->numerator, $other->unit->denominator);
172
            $unit->denominator = array_merge($unit->denominator, $other->unit->numerator);
173
            sort($unit->numerator);
174
            sort($unit->denominator);
175
            $unit->cancel();
176
        }
177
178
        return new self($value, $unit);
179
    }
180
181
    /**
182
     * Compares with another dimension.
183
     *
184
     * @param Node $other
185
     *
186
     * @return int
187
     */
188
    public function compare(Node $other)
189
    {
190
        if (!$other instanceof self) {
191
            return;
192
        }
193
194
        if ($this->unit->isEmpty() || $other->unit->isEmpty()) {
195
            $a = $this;
196
            $b = $other;
197
        } else {
198
            $a = $this->unify();
199
            $b = $other->unify();
200
            if ($a->unit->compare($b->unit) !== 0) {
201
                return;
202
            }
203
        }
204
205
        return Util::numericCompare($a->value, $b->value);
206
    }
207
208
    /**
209
     * Converts to the unified dimensions.
210
     *
211
     * @return DimensionNode
212
     */
213
    public function unify()
214
    {
215
        return $this->convertTo(
216
            [
217
                'length' => 'px',
218
                'duration' => 's',
219
                'angle' => 'rad',
220
            ]
221
        );
222
    }
223
224
    /**
225
     * Converts to another unit.
226
     *
227
     * @param array|string $conversions
228
     *
229
     * @return DimensionNode
230
     */
231
    public function convertTo($conversions)
232
    {
233
        $value = $this->value;
234
        $unit = clone $this->unit;
235
236
        if (is_string($conversions)) {
237
            $derivedConversions = [];
238
            foreach (UnitConversion::getGroups() as $i) {
239
                $group = UnitConversion::getGroup($i);
240
                if (isset($group[$conversions])) {
241
                    $derivedConversions = [$i => $conversions];
242
                }
243
            }
244
            $conversions = $derivedConversions;
245
        }
246
247
        foreach ($conversions as $groupName => $targetUnit) {
248
            $group = UnitConversion::getGroup($groupName);
249
            // numerator
250
            for ($i = 0, $count = count($unit->numerator); $i < $count; ++$i) {
251
                $atomicUnit = $unit->numerator[$i];
252
                if (is_object($atomicUnit)) {
253
                    continue;
254
                }
255
                if (!isset($group[$atomicUnit])) {
256
                    continue;
257
                }
258
                $value = $value * $group[$atomicUnit] / $group[$targetUnit];
259
                $unit->numerator[$i] = $targetUnit;
260
            }
261
262
            // denominator
263
            for ($i = 0, $count = count($unit->denominator); $i < $count; ++$i) {
264
                $atomicUnit = $unit->denominator[$i];
265
                if (!isset($group[$atomicUnit])) {
266
                    continue;
267
                }
268
                $value = $value / ($group[$atomicUnit] / $group[$targetUnit]);
269
                $unit->denominator[$i] = $targetUnit;
270
            }
271
        }
272
273
        $unit->cancel();
274
275
        return new self($value, $unit);
276
    }
277
}
278