FullCombination   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 171
Duplicated Lines 0 %

Test Coverage

Coverage 6.35%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 70
c 4
b 0
f 0
dl 0
loc 171
rs 9.68
ccs 4
cts 63
cp 0.0635
wmc 34

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A toPlainModifiersArray() 0 13 3
D modifyCharge() 0 72 20
A unique() 0 15 3
A sumCharges() 0 25 5
A isSuitable() 0 3 2
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-2020, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\php\billing\charge\modifiers;
12
13
use Exception;
14
use hiqdev\php\billing\action\ActionInterface;
15
use hiqdev\php\billing\charge\Charge;
16
use hiqdev\php\billing\charge\ChargeInterface;
17
use hiqdev\php\billing\charge\ChargeModifier;
18
use hiqdev\php\billing\charge\derivative\ChargeDerivative;
19
use hiqdev\php\billing\charge\derivative\ChargeDerivativeQuery;
20
21
use function count;
22
23
/**
24
 * Class FullCombination combines charges from all formulas from $left and $right parts of condition
25
 *
26
 * @author Dmytro Naumenko <[email protected]>
27
 */
28
class FullCombination implements ChargeModifier
29
{
30
    /**
31
     * @var ChargeModifier
32
     */
33
    protected $left;
34
    /**
35
     * @var ChargeModifier
36 1
     */
37
    protected $right;
38 1
39 1
    public function __construct(ChargeModifier $left, ChargeModifier $right)
40 1
    {
41
        $this->left = $left;
42
        $this->right = $right;
43
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    public function modifyCharge(?ChargeInterface $charge, ActionInterface $action): array
49
    {
50
        $leftCharges = [$charge];
51
        $rightCharges = [$charge];
52
53
        if ($this->left->isSuitable($charge, $action)) {
54
            $leftCharges = $this->left->modifyCharge($charge, $action);
55
56
            if ($charge && empty($leftCharges)) {
57
                return []; // If there was at least one charge, but it disappeared – modifier does not want this charge to happen. Stop.
58
            }
59
60
            $originalChargeExists = array_reduce($leftCharges, static function ($result, Charge $item) use ($charge) {
61
                return $result || $charge === $item;
62
            }, false);
63
            if ($charge && !$originalChargeExists) {
64
                return $leftCharges;
65
            }
66
            // $leftCharge will contain original charge and 0+ additional charges (discounts)
67
        }
68
69
        /** @var Charge $charge */
70
        $leftTotal = $this->sumCharges($charge, $leftCharges);
71
        if ($this->right->isSuitable($leftTotal, $action)) {
72
            /** @var Charge[] $dirtyRightCharges */
73
            $dirtyRightCharges = $this->right->modifyCharge($leftTotal, $action);
74
            if ($leftTotal && empty($dirtyRightCharges)) {
75
                return []; // If there was a charge, but it disappeared – modifier does not want this charge to happen. Stop.
76
            }
77
78
            $lastLeftCharge = end($leftCharges);
79
            $rightCharges = array_filter($dirtyRightCharges, function (ChargeInterface $charge) use ($leftTotal, $lastLeftCharge) {
80
                if ($charge->getParent() === $leftTotal) {
81
                    $charge->overwriteParent($lastLeftCharge);
82
                }
83
84
                // Drop $leftTotal from $rightCharges array
85
                return $charge !== $leftTotal;
86
            });
87
88
            if (count($rightCharges) === count($dirtyRightCharges)) {
89
                // Original $leftTotal was not returned. Left part will be discarded.
90
                // Dereference left charges from right charges parents
91
                $leftSplObjectIds = array_map(spl_object_id(...), $leftCharges);
0 ignored issues
show
Bug introduced by
The type hiqdev\php\billing\charge\modifiers\spl_object_id was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
92
                foreach ($rightCharges as $rightCharge) {
93
                    if ($rightCharge->getParent() !== null
94
                        && in_array(spl_object_id($rightCharge->getParent()), $leftSplObjectIds, true)
95
                    ) {
96
                        $rightCharge->overwriteParent(null);
97
                    }
98
                }
99
100
                return $rightCharges;
101
            }
102
        }
103
104
        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...
105
            // If we had both charge and left hand total charge – pass comments and events that were probably generated in left total
106
            if ($leftTotal->getComment()) {
107
                $charge->setComment($leftTotal->getComment());
108
            }
109
110
            /** @var Charge $leftTotal */
111
            $events = $leftTotal->releaseEvents();
112
            if (!empty($events)) {
113
                foreach ($events as $event) {
114
                    $charge->recordThat($event);
115
                }
116
            }
117
        }
118
119
        return $this->unique(array_merge($leftCharges, $rightCharges));
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125
    public function isSuitable(?ChargeInterface $charge, ActionInterface $action): bool
126
    {
127
        return $this->left->isSuitable($charge, $action) || $this->right->isSuitable($charge, $action);
128
    }
129
130
    /**
131
     * @param ChargeInterface[] $charges
132
     * @return ChargeInterface[] unique charges
133
     */
134
    private function unique(array $charges): array
135
    {
136
        $hashes = [];
137
        $result = [];
138
139
        foreach ($charges as $charge) {
140
            $hash = spl_object_hash($charge);
141
            if (isset($hashes[$hash])) {
142
                continue;
143
            }
144
            $hashes[$hash] = true;
145
            $result[] = $charge;
146
        }
147
148
        return $result;
149
    }
150
151
    /**
152
     * @param ChargeInterface $originalCharge
153
     * @param ChargeInterface[] $producedCharges
154
     * @throws Exception
155
     */
156
    private function sumCharges(?ChargeInterface $originalCharge, array $producedCharges): ?ChargeInterface
157
    {
158
        if ($originalCharge === null) {
159
            return null;
160
        }
161
162
        $sum = $originalCharge->getSum();
163
164
        $additionalCharges = [];
165
        foreach ($producedCharges as $charge) {
166
            if ($originalCharge === $charge) {
167
                continue;
168
            }
169
170
            $additionalCharges[] = $charge;
171
            $sum = $sum->add($charge->getSum());
172
        }
173
174
        if (empty($additionalCharges)) {
175
            return $originalCharge;
176
        }
177
178
        $query = (new ChargeDerivativeQuery())->changeSum($sum);
179
180
        return (new ChargeDerivative())->__invoke($originalCharge, $query);
181
    }
182
183
    /**
184
     * @return ChargeModifier[]
185
     */
186
    public function toPlainModifiersArray(): array
187
    {
188
        $result = [];
189
190
        foreach ([$this->left, $this->right] as $side) {
191
            if ($side instanceof FullCombination) {
192
                $result = array_merge($result, $side->toPlainModifiersArray());
193
            } else {
194
                $result[] = $side;
195
            }
196
        }
197
198
        return $result;
199
    }
200
}
201