Completed
Push — master ( 3ed755...5f3452 )
by Dmitry
02:24
created

FeatureContext::formulaIs()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
/**
3
 * PHP Billing Library
4
 *
5
 * @link      https://github.com/hiqdev/php-billing
6
 * @package   php-billing
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2017-2018, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\php\billing\tests\behat\bootstrap;
12
13
use Behat\Behat\Context\Context;
14
use DateTimeImmutable;
15
use hiqdev\php\billing\action\Action;
16
use hiqdev\php\billing\charge\Charge;
17
use hiqdev\php\billing\charge\ChargeInterface;
18
use hiqdev\php\billing\customer\Customer;
19
use hiqdev\php\billing\formula\FormulaEngine;
20
use hiqdev\php\billing\price\SinglePrice;
21
use hiqdev\php\billing\target\Target;
22
use hiqdev\php\billing\type\Type;
23
use hiqdev\php\units\Quantity;
24
use Money\Currencies\ISOCurrencies;
25
use Money\Parser\DecimalMoneyParser;
26
use NumberFormatter;
27
use PHPUnit\Framework\Assert;
28
29
/**
30
 * Defines application features from the specific context.
31
 */
32
class FeatureContext implements Context
33
{
34
    protected $engine;
35
36
    /** @var Customer */
37
    protected $customer;
38
    /**
39
     * @var \hiqdev\php\billing\price\PriceInterface|\hiqdev\php\billing\charge\FormulaChargeModifierTrait
40
     *
41
     * TODO: FormulaChargeModifierTrait::setFormula() must be moved to interface
42
     */
43
    protected $price;
44
    /**
45
     * @var \hiqdev\php\billing\action\ActionInterface|\hiqdev\php\billing\action\AbstractAction
46
     */
47
    protected $action;
48
    /**
49
     * @var ChargeInterface[]
50
     */
51
    protected $charges;
52
53
    /** @var \Money\MoneyParser */
54
    protected $moneyParser;
55
56
// Variable is not used. TODO: Remove?
57
//    protected $date;
58
59
    /**
60
     * Initializes context.
61
     */
62
    public function __construct()
63
    {
64
        $this->customer = new Customer(null, 'somebody');
65
        $this->moneyParser = new DecimalMoneyParser(new ISOCurrencies());
66
    }
67
68
    /**
69
     * @Given /(\S+) (\S+) price is ([0-9.]+) (\w+) per (\w+)/
70
     */
71
    public function priceIs($target, $type, $sum, $currency, $unit)
72
    {
73
        $type = new Type(Type::ANY, $type);
74
        $target = new Target(Target::ANY, $target);
75
        $quantity = Quantity::create($unit, 0);
76
        $sum = $this->moneyParser->parse($sum, $currency);
77
        $this->price = new SinglePrice(null, $type, $target, null, $quantity, $sum);
78
    }
79
80
    /**
81
     * @Given /action is (\S+) (\w+) ([0-9.]+) (\S+)/
82
     */
83
    public function actionIs($target, $type, $amount, $unit)
84
    {
85
        $type = new Type(Type::ANY, $type);
86
        $target = new Target(Target::ANY, $target);
87
        $quantity = Quantity::create($unit, $amount);
88
        $time = new DateTimeImmutable();
89
        $this->action = new Action(null, $type, $target, $quantity, $this->customer, $time);
90
    }
91
92
    /**
93
     * @Given /formula is (.+)/
94
     */
95
    public function formulaIs($formula)
96
    {
97
        $this->price->setFormula($this->getFormulaEngine()->build($formula));
0 ignored issues
show
Bug introduced by
The method setFormula does only exist in hiqdev\php\billing\charg...mulaChargeModifierTrait, but not in hiqdev\php\billing\price\PriceInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
98
    }
99
100
    protected function getFormulaEngine()
101
    {
102
        if ($this->engine === null) {
103
            $this->engine = new FormulaEngine();
104
        }
105
106
        return $this->engine;
107
    }
108
109
    /**
110
     * @When /action date is ([0-9.-]+)/
111
     */
112
    public function actionDateIs($date)
113
    {
114
        $this->action->setTime(new DateTimeImmutable($date));
0 ignored issues
show
Bug introduced by
The method setTime does only exist in hiqdev\php\billing\action\AbstractAction, but not in hiqdev\php\billing\action\ActionInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
115
    }
116
117
    /**
118
     * @Then /^(\w+) charge is $/
119
     */
120
    public function emptyCharge($numeral)
121
    {
122
        $this->chargeIs($numeral);
123
    }
124
125
    /**
126
     * @Then /^(\w+) charge is (\S+) ([0-9.]+) ([A-Z]{3})$/
127
     */
128
    public function chargeWithSum($numeral, $type = null, $sum = null, $currency = null)
129
    {
130
        $this->chargeIs($numeral, $type, $sum, $currency);
131
    }
132
133
    /**
134
     * @Then /^(\w+) charge is (\S+) ([0-9.]+) ([A-Z]{3}) reason (.+)/
135
     */
136
    public function chargeWithReason($numeral, $type = null, $sum = null, $currency = null, $reason = null)
137
    {
138
        $this->chargeIs($numeral, $type, $sum, $currency, $reason);
139
    }
140
141
    public function chargeIs($numeral, $type = null, $sum = null, $currency = null, $reason = null)
142
    {
143
        $no = $this->ensureNo($numeral);
144
        if ($no === 0) {
145
            $this->charges = $this->price->calculateCharges($this->action);
0 ignored issues
show
Bug introduced by
The method calculateCharges does only exist in hiqdev\php\billing\price\PriceInterface, but not in hiqdev\php\billing\charg...mulaChargeModifierTrait.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
146
        }
147
        $this->assertCharge($this->charges[$no] ?? null, $type, $sum, $currency, $reason);
148
    }
149
150
    /**
151
     * @param ChargeInterface|null $charge
152
     * @param string|null $type
153
     * @param string|null $sum
154
     * @param string|null $currency
155
     * @param string|null $reason
156
     */
157
    public function assertCharge($charge, $type, $sum, $currency, $reason)
158
    {
159
        if (empty($type) && empty($sum) && empty($currency)) {
160
            Assert::assertNull($charge);
161
            return;
162
        }
163
        Assert::assertInstanceOf(Charge::class, $charge);
164
        Assert::assertSame($type, $charge->getPrice()->getType()->getName());
0 ignored issues
show
Bug introduced by
It seems like $charge is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
165
        $money = $this->moneyParser->parse($sum, $currency);
166
        Assert::assertEquals($money, $charge->getSum()); // TODO: Should we add `getSum()` to ChargeInterface?
167
        if ($reason !== null) {
168
            Assert::assertSame($reason, $charge->getComment()); // TODO: Should we add `getComment()` to ChargeInterface?
169
        }
170
    }
171
172
    private function ensureNo(string $numeral): int
173
    {
174
        $formatter = new NumberFormatter('en_EN', NumberFormatter::SPELLOUT);
175
        $result = $formatter->parse($numeral);
176
        if ($result === false) {
177
            throw new \Exception("Wrong numeral '$numeral'");
178
        }
179
180
        return --$result;
181
    }
182
}
183