Completed
Pull Request — master (#63)
by Adam
03:45
created

Real::subtract()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
ccs 2
cts 2
cp 1
rs 9.4285
cc 1
eloc 2
nc 1
nop 1
crap 1
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 4
    public static function fromNative()
22
    {
23 4
        $value = func_get_arg(0);
24
25 4
        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 147
    public function __construct($value)
34
    {
35 147
        $value = \filter_var($value, FILTER_VALIDATE_FLOAT);
36
37 147
        if (FALSE === $value) {
38 1
            throw new InvalidNativeArgumentException($value, ['float']);
39
        }
40
41 146
        $this->value = $value;
42 146
    }
43
44
    /**
45
     * Returns the native value of the real number
46
     *
47
     * @return float
48
     */
49 138
    public function toNative()
50
    {
51 138
        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
        }
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(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(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(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(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(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(bcpow($this->toNative(), 2, 5));
182
    }
183
184
    /**
185
     * @return MathAwareInterface
186
     */
187 1
    public function squareRoot()
188
    {
189 1
        return new static(bcsqrt($this, 5));
190
    }
191
192
193
    /**
194
     * @return MathAwareInterface
195
     */
196 1
    public function cube()
197
    {
198 1
        return new static(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(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