Money::getAmount()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
crap 1
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
    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...
135
    {
136
        return [
137
            'amount' => $this->getAmount(),
138
            'currency' => $this->getCurrency()
139
        ];
140
    }
141
142
143
}
144