Completed
Push — master ( 5de933...1b4bc5 )
by Dmitry
04:39
created

FullCombination::modifyCharge()   D

Complexity

Conditions 18
Paths 28

Size

Total Lines 66
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 342

Importance

Changes 0
Metric Value
cc 18
eloc 33
nc 28
nop 2
dl 0
loc 66
ccs 0
cts 33
cp 0
crap 342
rs 4.8666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
            // $leftCharge will contain original charge and 0+ additional charges (discounts)
62
        }
63
64
        /** @var Charge $leftTotal */
65
        /** @var Charge $charge */
66
        $leftTotal = $this->sumCharges($charge, $leftCharges);
67
        if ($this->right->isSuitable($leftTotal, $action)) {
68
            /** @var Charge[] $dirtyRightCharges */
69
            $dirtyRightCharges = $this->right->modifyCharge($leftTotal, $action);
70
            if ($leftTotal && empty($dirtyRightCharges)) {
71
                return []; // If there was a charge, but it disappeared – modifier does not want this charge to happen. Stop.
72
            }
73
74
            $lastLeftCharge = end($leftCharges);
75
            $rightCharges = array_filter($dirtyRightCharges, function (ChargeInterface $charge) use ($leftTotal, $lastLeftCharge) {
76
                /** @var Charge $charge */
77
                if ($charge->getParent() === $leftTotal) {
78
                    $charge->overwriteParent($lastLeftCharge);
79
                }
80
81
                // Drop $leftTotal from $rightCharges array
82
                return $charge !== $leftTotal;
83
            });
84
85
            if (\count($rightCharges) === \count($dirtyRightCharges)) { // Original $leftTotal was not returned
86
                foreach ($rightCharges as $rightCharge) {
87
                    $rightCharge->overwriteParent($charge);
88
                }
89
90
                return $rightCharges;
91
            }
92
        }
93
94
        if ($charge && $leftTotal) {
0 ignored issues
show
introduced by
$leftTotal is of type hiqdev\php\billing\charge\ChargeInterface, thus it always evaluated to true.
Loading history...
95
            // If we had both charge and left hand total charge – pass comments and events that were probably generated in left total
96
            if ($leftTotal->getComment()) {
97
                $charge->setComment($leftTotal->getComment());
98
            }
99
100
            $events = $leftTotal->releaseEvents();
0 ignored issues
show
Bug introduced by
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

100
            /** @scrutinizer ignore-call */ 
101
            $events = $leftTotal->releaseEvents();
Loading history...
101
            if (!empty($events)) {
102
                foreach ($events as $event) {
103
                    $charge->recordThat($event);
104
                }
105
            }
106
        }
107
108
        return $this->unique(array_merge($leftCharges, $rightCharges));
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114
    public function isSuitable(?ChargeInterface $charge, ActionInterface $action): bool
115
    {
116
        return $this->left->isSuitable($charge, $action) || $this->right->isSuitable($charge, $action);
117
    }
118
119
    /**
120
     * @param ChargeInterface[] $charges
121
     * @return ChargeInterface[] unique charges
122
     */
123
    private function unique(array $charges): array
124
    {
125
        $hashes = [];
126
        $result = [];
127
128
        foreach ($charges as $charge) {
129
            $hash = spl_object_hash($charge);
130
            if (isset($hashes[$hash])) {
131
                continue;
132
            }
133
            $hashes[$hash] = true;
134
            $result[] = $charge;
135
        }
136
137
        return $result;
138
    }
139
140
    /**
141
     * @param ChargeInterface $originalCharge
142
     * @param ChargeInterface[] $producedCharges
143
     * @return ChargeInterface|null
144
     * @throws \Exception
145
     */
146
    private function sumCharges(?ChargeInterface $originalCharge, array $producedCharges): ?ChargeInterface
147
    {
148
        if ($originalCharge === null) {
149
            return null;
150
        }
151
152
        $sum = $originalCharge->getSum();
153
154
        $additionalCharges = [];
155
        foreach ($producedCharges as $charge) {
156
            if ($originalCharge === $charge) {
157
                continue;
158
            }
159
160
            $additionalCharges[] = $charge;
161
            $sum = $sum->add($charge->getSum());
162
        }
163
164
        if (empty($additionalCharges)) {
165
            return $originalCharge;
166
        }
167
168
        $tempCharge = new Charge(
169
            $originalCharge->getId(),
170
            $originalCharge->getType(),
171
            $originalCharge->getTarget(),
172
            $originalCharge->getAction(),
173
            $originalCharge->getPrice(),
174
            $originalCharge->getUsage(),
175
            $sum,
176
            $originalCharge->getBill()
0 ignored issues
show
Bug introduced by
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

176
            $originalCharge->/** @scrutinizer ignore-call */ 
177
                             getBill()
Loading history...
177
        );
178
        if ($originalCharge->getComment() !== null) {
179
            $tempCharge->setComment($originalCharge->getComment());
180
        }
181
        if ($originalCharge->getParent() !== null) {
182
            $tempCharge->setParent($originalCharge->getParent());
183
        }
184
185
        return $tempCharge;
186
    }
187
}
188