Money::sameValueAs()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 4
nc 3
nop 1
crap 3
1
<?php
2
3
namespace ValueObjects\Money;
4
5
use Money\Money as BaseMoney;
6
use Money\Currency as BaseCurrency;
7
use ValueObjects\Number\Integer;
8
use ValueObjects\Number\Real;
9
use ValueObjects\Number\RoundingMode;
10
use ValueObjects\Util\Util;
11
use ValueObjects\ValueObjectInterface;
12
13
class Money implements ValueObjectInterface
14
{
15
    /** @var BaseMoney */
16
    protected $money;
17
18
    /** @var Currency */
19
    protected $currency;
20
21
    /**
22
     * Returns a Money object from native int amount and string currency code
23
     *
24
     * @param  int    $amount   Amount expressed in the smallest units of $currency (e.g. cents)
0 ignored issues
show
Bug introduced by
There is no parameter named $amount. 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...
25
     * @param  string $currency Currency code of the money object
0 ignored issues
show
Bug introduced by
There is no parameter named $currency. 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...
26
     * @return static
27
     */
28 1
    public static function fromNative()
29
    {
30 1
        $args = func_get_args();
31
32 1
        $amount   = new Integer($args[0]);
33 1
        $currency = Currency::fromNative($args[1]);
34
35 1
        return new static($amount, $currency);
36
    }
37
38
    /**
39
     * Returns a Money object
40
     *
41
     * @param \ValueObjects\Number\Integer $amount   Amount expressed in the smallest units of $currency (e.g. cents)
42
     * @param Currency                     $currency Currency of the money object
43
     */
44 9
    public function __construct(Integer $amount, Currency $currency)
45
    {
46 9
        $baseCurrency   = new BaseCurrency($currency->getCode()->toNative());
47 9
        $this->money    = new BaseMoney($amount->toNative(), $baseCurrency);
48 9
        $this->currency = $currency;
49 9
    }
50
51
    /**
52
     *  Tells whether two Currency are equal by comparing their amount and currency
53
     *
54
     * @param  ValueObjectInterface $money
55
     * @return bool
56
     */
57 2
    public function sameValueAs(ValueObjectInterface $money)
58
    {
59 2
        if (false === Util::classEquals($this, $money)) {
60 1
            return false;
61
        }
62
63 2
        return $this->getAmount()->sameValueAs($money->getAmount()) && $this->getCurrency()->sameValueAs($money->getCurrency());
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 getAmount() does only exist in the following implementations of said interface: ValueObjects\Money\Money.

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...
Bug introduced by
It seems like you code against a concrete implementation and not the interface ValueObjects\ValueObjectInterface as the method getCurrency() does only exist in the following implementations of said interface: ValueObjects\Money\Money.

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...
64
    }
65
66
    /**
67
     * Returns money amount
68
     *
69
     * @return \ValueObjects\Number\Integer
70
     */
71 8
    public function getAmount()
72
    {
73 8
        $amount = new Integer($this->money->getAmount());
74
75 8
        return $amount;
76
    }
77
78
    /**
79
     * Returns money currency
80
     *
81
     * @return Currency
82
     */
83 8
    public function getCurrency()
84
    {
85 8
        return clone $this->currency;
86
    }
87
88
    /**
89
     * Add an integer quantity to the amount and returns a new Money object.
90
     * Use a negative quantity for subtraction.
91
     *
92
     * @param  \ValueObjects\Number\Integer $quantity Quantity to add
93
     * @return Money
94
     */
95 2
    public function add(Integer $quantity)
96
    {
97 2
        $amount = new Integer($this->getAmount()->toNative() + $quantity->toNative());
98 2
        $result = new static($amount, $this->getCurrency());
99
100 2
        return $result;
101
    }
102
103
    /**
104
     * Multiply the Money amount for a given number and returns a new Money object.
105
     * Use 0 < Real $multipler < 1 for division.
106
     *
107
     * @param  Real  $multiplier
108
     * @param  mixed $rounding_mode Rounding mode of the operation. Defaults to RoundingMode::HALF_UP.
109
     * @return Money
110
     */
111 2
    public function multiply(Real $multiplier, RoundingMode $rounding_mode = null)
112
    {
113 2
        if (null === $rounding_mode) {
114 2
            $rounding_mode = RoundingMode::HALF_UP();
115 2
        }
116
117 2
        $amount        = $this->getAmount()->toNative() * $multiplier->toNative();
118 2
        $roundedAmount = new Integer(round($amount, 0, $rounding_mode->toNative()));
119 2
        $result        = new static($roundedAmount, $this->getCurrency());
120
121 2
        return $result;
122
    }
123
124
    /**
125
     * Returns a string representation of the Money value in format "CUR AMOUNT" (e.g.: EUR 1000)
126
     *
127
     * @return string
128
     */
129 1
    public function __toString()
130
    {
131 1
        return \sprintf('%s %d', $this->getCurrency()->getCode(), $this->getAmount()->toNative());
132
    }
133
}
134