Real   B
last analyzed

Complexity

Total Complexity 43

Size/Duplication

Total Lines 380
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 95.92%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 43
lcom 1
cbo 5
dl 0
loc 380
ccs 94
cts 98
cp 0.9592
rs 8.3157
c 1
b 0
f 0

38 Methods

Rating   Name   Duplication   Size   Complexity  
A fromNative() 0 6 1
A __construct() 0 10 2
A toNative() 0 4 1
A sameValueAs() 0 8 2
A toInteger() 0 12 2
A toNatural() 0 8 1
A __toString() 0 4 1
A pi() 0 4 1
A add() 0 4 1
A subtract() 0 5 1
A multiply() 0 5 1
A divide() 0 6 1
A toPower() 0 5 1
A square() 0 4 1
A squareRoot() 0 4 1
A cube() 0 5 1
A isEven() 0 4 1
A isOdd() 0 4 1
A factorial() 0 11 2
A isGreaterThan() 0 4 1
A isGreaterOrEqual() 0 5 1
A isLessThan() 0 5 1
A isLessOrEqual() 0 5 1
A modulo() 0 4 1
A isZero() 0 4 1
A isPositive() 0 5 1
A isNegative() 0 4 1
A digits() 0 4 1
A inverse() 0 5 1
A increment() 0 4 1
A decrement() 0 5 1
A by10() 0 5 1
A by100() 0 5 1
A scientific() 0 4 1
A zero() 0 4 1
A random() 0 4 1
A absolute() 0 8 2
A jsonSerialize() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Real 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Real, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace ValueObjects\Number;
4
5
use ValueObjects\Exception\InvalidNativeArgumentException;
6
use ValueObjects\StringLiteral\StringLiteral;
7
use ValueObjects\Util\Util;
8
use ValueObjects\ValueObjectInterface;
9
10
class Real implements NumberInterface, MathAwareInterface
11
{
12
    protected $value;
13
14
    /**
15
     * Returns a Real object given a PHP native float as parameter.
16
     *
17
     * @param  float $number
0 ignored issues
show
Bug introduced by
There is no parameter named $number. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
18
     *
19
     * @return static
20
     */
21 5
    public static function fromNative()
22
    {
23 5
        $value = func_get_arg(0);
24
25 5
        return new static($value);
26
    }
27
28
    /**
29
     * Returns a Real object given a PHP native float as parameter.
30
     *
31
     * @param float $number
0 ignored issues
show
Bug introduced by
There is no parameter named $number. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
32
     */
33 149
    public function __construct($value)
34
    {
35 149
        $value = \filter_var($value, FILTER_VALIDATE_FLOAT);
36
37 149
        if (FALSE === $value) {
38 1
            throw new InvalidNativeArgumentException($value, ['float']);
39
        }
40
41 148
        $this->value = $value;
42 148
    }
43
44
    /**
45
     * Returns the native value of the real number
46
     *
47
     * @return float
48
     */
49 140
    public function toNative()
50
    {
51 140
        return $this->value;
52
    }
53
54
    /**
55
     * Tells whether two Real are equal by comparing their values
56
     *
57
     * @param  ValueObjectInterface $real
58
     *
59
     * @return bool
60
     */
61 15
    public function sameValueAs(ValueObjectInterface $real)
62
    {
63 15
        if (FALSE === Util::classEquals($this, $real)) {
64 1
            return FALSE;
65
        }
66
67 15
        return $this->toNative() === $real->toNative();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface ValueObjects\ValueObjectInterface as the method toNative() does only exist in the following implementations of said interface: ValueObjects\Climate\Celsius, ValueObjects\Climate\Fahrenheit, ValueObjects\Climate\Kelvin, ValueObjects\Climate\RelativeHumidity, ValueObjects\Climate\Temperature, ValueObjects\DateTime\Hour, ValueObjects\DateTime\Minute, ValueObjects\DateTime\Month, ValueObjects\DateTime\MonthDay, ValueObjects\DateTime\Second, ValueObjects\DateTime\WeekDay, ValueObjects\DateTime\Year, ValueObjects\Enum\Enum, ValueObjects\Geography\Continent, ValueObjects\Geography\CountryCode, ValueObjects\Geography\DistanceFormula, ValueObjects\Geography\DistanceUnit, ValueObjects\Geography\Ellipsoid, ValueObjects\Geography\Latitude, ValueObjects\Geography\Longitude, ValueObjects\Identity\UUID, ValueObjects\Money\CurrencyCode, ValueObjects\Number\Complex, ValueObjects\Number\Integer, ValueObjects\Number\Natural, ValueObjects\Number\Real, ValueObjects\Number\RoundingMode, ValueObjects\Person\Age, ValueObjects\Person\Gender, ValueObjects\StringLiteral\StringLiteral, ValueObjects\Web\Domain, ValueObjects\Web\EmailAddress, ValueObjects\Web\FragmentIdentifier, ValueObjects\Web\Hostname, ValueObjects\Web\IPAddress, ValueObjects\Web\IPAddressVersion, ValueObjects\Web\IPv4Address, ValueObjects\Web\IPv6Address, ValueObjects\Web\NullFragmentIdentifier, ValueObjects\Web\NullQueryString, ValueObjects\Web\Path, ValueObjects\Web\PortNumber, ValueObjects\Web\QueryString, ValueObjects\Web\SchemeName.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
68
    }
69
70
    /**
71
     * Returns the integer part of the Real number as a Integer
72
     *
73
     * @param  RoundingMode $rounding_mode Rounding mode of the conversion. Defaults to RoundingMode::HALF_UP.
74
     *
75
     * @return Integer
76
     */
77 2
    public function toInteger(RoundingMode $rounding_mode = NULL)
78
    {
79 2
        if (NULL === $rounding_mode) {
80 2
            $rounding_mode = RoundingMode::HALF_UP();
81 2
        }
82
83 2
        $value = $this->toNative();
84 2
        $integerValue = \round($value, 0, $rounding_mode->toNative());
85 2
        $integer = new Integer($integerValue);
86
87 2
        return $integer;
88
    }
89
90
    /**
91
     * Returns the absolute integer part of the Real number as a Natural
92
     *
93
     * @param  RoundingMode $rounding_mode Rounding mode of the conversion. Defaults to RoundingMode::HALF_UP.
94
     *
95
     * @return Natural
96
     */
97 1
    public function toNatural(RoundingMode $rounding_mode = NULL)
98
    {
99 1
        $integerValue = $this->toInteger($rounding_mode)->toNative();
100 1
        $naturalValue = \abs($integerValue);
101 1
        $natural = new Natural($naturalValue);
102
103 1
        return $natural;
104
    }
105
106
    /**
107
     * Returns the string representation of the real value
108
     *
109
     * @return string
110
     */
111 26
    public function __toString()
112
    {
113 26
        return \strval($this->toNative());
114
    }
115
116
    static public function pi()
117
    {
118
        return self::fromNative(M_PI);
119
    }
120
121
    /**
122
     * @param \ValueObjects\Number\MathAwareInterface $number
123
     *
124
     * @return MathAwareInterface
125
     */
126 2
    public function add(MathAwareInterface $number)
127
    {
128 2
        return new static((float) bcadd($this, $number, 5));
129
    }
130
131
    /**
132
     * @param \ValueObjects\Number\MathAwareInterface $number
133
     *
134
     * @return MathAwareInterface
135
     */
136 2
    public function subtract(MathAwareInterface $number)
137
    {
138 2
        return new static((float) bcsub($this, $number, 5));
139
140
    }
141
142
    /**
143
     * @param \ValueObjects\Number\MathAwareInterface $number
144
     *
145
     * @return MathAwareInterface
146
     */
147 3
    public function multiply(MathAwareInterface $number)
148
    {
149 3
        return new static((float) bcmul($this, $number, 5));
150
151
    }
152
153
    /**
154
     * @param \ValueObjects\Number\MathAwareInterface $number
155
     *
156
     * @return MathAwareInterface
157
     */
158 3
    public function divide(MathAwareInterface $number)
159
    {
160 3
        return new static((float) bcdiv($this, $number, 5));
161
162
163
    }
164
165
    /**
166
     * @param \ValueObjects\Number\MathAwareInterface $number
167
     *
168
     * @return MathAwareInterface
169
     */
170 1
    public function toPower(MathAwareInterface $number)
171
    {
172 1
        return new static((float) bcpow($this->toNative(), $number->toNative(), 5));
173
174
    }
175
176
    /**
177
     * @return MathAwareInterface
178
     */
179 1
    public function square()
180
    {
181 1
        return new static((float) bcpow($this->toNative(), 2, 5));
182
    }
183
184
    /**
185
     * @return MathAwareInterface
186
     */
187 1
    public function squareRoot()
188
    {
189 1
        return new static((float) bcsqrt($this, 5));
190
    }
191
192
193
    /**
194
     * @return MathAwareInterface
195
     */
196 1
    public function cube()
197
    {
198 1
        return new static((float) bcpow($this->toNative(), 3, 5));
199
200
    }
201
202
    /**
203
     * @return bool
204
     */
205 2
    public function isEven()
206
    {
207 2
        return "0" === bcmod($this, 2);
208
    }
209
210
    /**
211
     * @return bool
212
     */
213 1
    public function isOdd()
214
    {
215 1
        return (FALSE === $this->isEven());
216
    }
217
218
    /**
219
     * @return MathAwareInterface
220
     */
221 1
    public function factorial()
222
    {
223
224 1
        $number = $this->toNative();
225 1
        $factorial = 1;
226 1
        for ($index = 1; $index <= $number; $index++)
227 1
            $factorial = bcmul($factorial, $index);
228
229 1
        return new static($factorial);
230
231
    }
232
233
    /**
234
     * @param \ValueObjects\Number\MathAwareInterface $number
235
     *
236
     * @return bool
237
     */
238 2
    public function isGreaterThan(MathAwareInterface $number)
239
    {
240 2
        return 1 == bccomp($this, $number);
241
    }
242
243
    /**
244
     * @param \ValueObjects\Number\MathAwareInterface $number
245
     *
246
     * @return bool
247
     */
248 1
    public function isGreaterOrEqual(MathAwareInterface $number)
249
    {
250 1
        return 0 <= bccomp($this, $number);
251
252
    }
253
254
    /**
255
     * @param \ValueObjects\Number\MathAwareInterface $number
256
     *
257
     * @return bool
258
     */
259 2
    public function isLessThan(MathAwareInterface $number)
260
    {
261 2
        return -1 == bccomp($this, $number);
262
263
    }
264
265
    /**
266
     * @param \ValueObjects\Number\MathAwareInterface $number
267
     *
268
     * @return bool
269
     */
270 1
    public function isLessOrEqual(MathAwareInterface $number)
271
    {
272 1
        return 0 >= bccomp($this, $number);
273
274
    }
275
276
    /**
277
     * @param \ValueObjects\Number\MathAwareInterface $number
278
     *
279
     * @return MathAwareInterface
280
     */
281 1
    public function modulo(MathAwareInterface $number)
282
    {
283 1
        return new static((float) bcmod($this, $number));
284
    }
285
286
    /**
287
     * @return bool
288
     */
289 1
    public function isZero()
290
    {
291 1
        return 0 === bccomp($this, "0");
292
    }
293
294
    /**
295
     * @return bool
296
     */
297 1
    public function isPositive()
298
    {
299 1
        return 1 === bccomp($this, "0");
300
301
    }
302
303
    /**
304
     * @return bool
305
     */
306 2
    public function isNegative()
307
    {
308 2
        return -1 === bccomp($this, "0");
309
    }
310
311
    /**
312
     * @return MathAwareInterface
313
     */
314 1
    public function digits()
315
    {
316 1
        return new static(strlen($this));
317
    }
318
319
    /**
320
     * @return MathAwareInterface
321
     */
322 2
    public function inverse()
323
    {
324 2
        return $this->multiply(new Real(-1));
325
326
    }
327
328 1
    public function increment()
329
    {
330 1
        return $this->add(new Real(1));
331
    }
332
333 1
    public function decrement()
334
    {
335 1
        return $this->subtract(new Real(1));
336
337
    }
338
339 1
    public function by10()
340
    {
341 1
        return $this->divide(new Real(10));
342
343
    }
344
345 1
    public function by100()
346
    {
347 1
        return $this->divide(new Real(100));
348
349
    }
350
351
    /**
352
     * @return StringLiteral
353
     */
354 1
    public function scientific()
355
    {
356 1
        return new StringLiteral(sprintf('%e', $this->toNative()));
357
    }
358
359 1
    public static function zero()
360
    {
361 1
        return new static(0);
362
    }
363
364
    public static function random($min = NULL, $max = NULL)
365
    {
366
        return new static(mt_rand($min, $max));
367
    }
368
369 1
    public function absolute()
370
    {
371 1
        if($this->isNegative()) {
372 1
            return $this->inverse();
373
        }
374
375 1
        return $this;
376
    }
377
378
379
380
    /**
381
     * @return float
382
     */
383 1
    function jsonSerialize()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for jsonSerialize.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
384
    {
385 1
        return $this->toNative();
386
    }
387
388
389
}
390