Completed
Push — master ( c0f64f...1cd2ee )
by Dmitry
02:34
created

FullCombination::sumCharges()   B

Complexity

Conditions 7
Paths 16

Size

Total Lines 40
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
cc 7
eloc 25
nc 16
nop 2
dl 0
loc 40
ccs 0
cts 26
cp 0
crap 56
rs 8.5866
c 0
b 0
f 0
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\charge\modifiers;
12
13
use hiqdev\php\billing\action\ActionInterface;
14
use hiqdev\php\billing\charge\Charge;
15
use hiqdev\php\billing\charge\ChargeInterface;
16
use hiqdev\php\billing\charge\ChargeModifier;
17
18
/**
19
 * Class FullCombination combines charges from all formulas from $left and $right parts of condition
20
 *
21
 * @author Dmytro Naumenko <[email protected]>
22
 */
23
class FullCombination implements ChargeModifier
24
{
25
    /**
26
     * @var ChargeModifier
27
     */
28
    protected $left;
29
    /**
30
     * @var ChargeModifier
31
     */
32
    protected $right;
33
34 1
    public function __construct(ChargeModifier $left, ChargeModifier $right)
35
    {
36 1
        $this->left = $left;
37 1
        $this->right = $right;
38 1
    }
39
40
    /**
41
     * {@inheritdoc}
42
     */
43
    public function modifyCharge(?ChargeInterface $charge, ActionInterface $action): array
44
    {
45
        $leftCharges = [$charge];
46
        $rightCharges = [$charge];
47
48
        if ($this->left->isSuitable($charge, $action)) {
49
            $leftCharges = $this->left->modifyCharge($charge, $action);
50
51
            if ($charge && empty($leftCharges)) {
52
                return []; // If there was at least one charge, but it disappeared – modifier does not want this charge to happen. Stop.
53
            }
54
55
            $originalChargeExists = array_reduce($leftCharges, function ($result, Charge $item) use ($charge) {
56
                return $result || $charge === $item;
57
            }, false);
58
            if ($charge && !$originalChargeExists) {
59
                return $leftCharges;
60
            }
61
        }
62
63
        /** @var Charge $leftTotal */
64
        /** @var Charge $charge */
65
        $leftTotal = $this->sumCharges($charge, $leftCharges);
66
        if ($this->right->isSuitable($leftTotal, $action)) {
67
            $dirtyRightCharges = $this->right->modifyCharge($leftTotal, $action);
68
            if ($leftTotal && empty($dirtyRightCharges)) {
69
                return []; // If there was a charge, but it disappeared – modifier does not want this charge to happen. Stop.
70
            }
71
72
            // Drop $leftTotal from $rightCharges array
73
            $rightCharges = array_filter($dirtyRightCharges, function (ChargeInterface $charge) use ($leftTotal) {
74
                return $charge !== $leftTotal;
75
            });
76
77
            if (\count($rightCharges) === \count($dirtyRightCharges)) { // Original $leftTotal was not returned
78
                return $rightCharges;
79
            }
80
        }
81
82
        if ($charge && $leftTotal) {
0 ignored issues
show
introduced by SilverFire - Dmitry Naumenko
$leftTotal is of type hiqdev\php\billing\charge\ChargeInterface, thus it always evaluated to true.
Loading history...
83
            // If we had charge and left hand total charge – pass comments and events that were probably generated in left total
84
            if ($leftTotal->getComment()) {
85
                $charge->setComment($leftTotal->getComment());
86
            }
87
88
            $events = $leftTotal->releaseEvents();
0 ignored issues
show
Bug introduced by SilverFire - Dmitry Naumenko
The method releaseEvents() does not exist on hiqdev\php\billing\charge\ChargeInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to hiqdev\php\billing\charge\ChargeInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

88
            /** @scrutinizer ignore-call */ 
89
            $events = $leftTotal->releaseEvents();
Loading history...
89
            if (!empty($events)) {
90
                foreach ($events as $event) {
91
                    $charge->recordThat($event);
92
                }
93
            }
94
        }
95
96
        return $this->unique(array_merge($leftCharges, $rightCharges));
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102
    public function isSuitable(?ChargeInterface $charge, ActionInterface $action): bool
103
    {
104
        return $this->left->isSuitable($charge, $action) || $this->right->isSuitable($charge, $action);
105
    }
106
107
    /**
108
     * @param ChargeInterface[] $charges
109
     * @return ChargeInterface[] unique charges
110
     */
111
    private function unique(array $charges): array
112
    {
113
        $hashes = [];
114
        $result = [];
115
116
        foreach ($charges as $charge) {
117
            $hash = spl_object_hash($charge);
118
            if (isset($hashes[$hash])) {
119
                continue;
120
            }
121
            $hashes[$hash] = true;
122
            $result[] = $charge;
123
        }
124
125
        return $result;
126
    }
127
128
    /**
129
     * @param ChargeInterface $originalCharge
130
     * @param ChargeInterface[] $producedCharges
131
     * @return ChargeInterface|null
132
     * @throws \Exception
133
     */
134
    private function sumCharges(?ChargeInterface $originalCharge, array $producedCharges): ?ChargeInterface
135
    {
136
        if ($originalCharge === null) {
137
            return null;
138
        }
139
140
        $sum = $originalCharge->getSum();
141
142
        $additionalCharges = [];
143
        foreach ($producedCharges as $charge) {
144
            if ($originalCharge === $charge) {
145
                continue;
146
            }
147
148
            $additionalCharges[] = $charge;
149
            $sum = $sum->add($charge->getSum());
150
        }
151
152
        if (empty($additionalCharges)) {
153
            return $originalCharge;
154
        }
155
156
        $tempCharge = new Charge(
157
            $originalCharge->getId(),
158
            $originalCharge->getType(),
159
            $originalCharge->getTarget(),
160
            $originalCharge->getAction(),
161
            $originalCharge->getPrice(),
162
            $originalCharge->getUsage(),
163
            $sum,
164
            $originalCharge->getBill()
0 ignored issues
show
Bug introduced by SilverFire - Dmitry Naumenko
The method getBill() does not exist on hiqdev\php\billing\charge\ChargeInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to hiqdev\php\billing\charge\ChargeInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

164
            $originalCharge->/** @scrutinizer ignore-call */ 
165
                             getBill()
Loading history...
165
        );
166
        if ($originalCharge->getComment() !== null) {
167
            $tempCharge->setComment($originalCharge->getComment());
168
        }
169
        if ($originalCharge->getParent() !== null) {
170
            $tempCharge->setParent($originalCharge->getParent());
171
        }
172
173
        return $tempCharge;
174
    }
175
}
176