Price   A
last analyzed

Complexity

Total Complexity 41

Size/Duplication

Total Lines 245
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 67
c 3
b 0
f 0
dl 0
loc 245
rs 9.1199
wmc 41

39 Methods

Rating   Name   Duplication   Size   Complexity  
A setSchedule() 0 3 1
A setExternalReference() 0 3 1
A getAmount() 0 3 1
A hasExternalReference() 0 3 1
A setCurrency() 0 3 1
A setAmount() 0 3 1
A setIncludingTax() 0 3 1
A getCurrency() 0 3 1
A setId() 0 3 1
A getId() 0 3 1
A isRecurring() 0 3 1
A getSchedule() 0 3 1
A setRecurring() 0 3 1
A getExternalReference() 0 3 1
A isIncludingTax() 0 3 1
A getIsDeleted() 0 3 1
A getCreatedAt() 0 3 1
A getPaymentProviderDetailsUrl() 0 3 1
A setType() 0 3 1
A getAsMoney() 0 7 2
A getUnits() 0 3 1
A setIsDeleted() 0 3 1
A setDeletedAt() 0 3 1
A markAsDeleted() 0 6 1
A getType() 0 3 1
A setTierComponents() 0 3 1
A setProduct() 0 3 1
A setCreatedAt() 0 3 1
A unmarkAsDeleted() 0 6 1
A setUnits() 0 3 1
A getDisplayName() 0 9 2
A setUsage() 0 3 1
A isPublic() 0 3 1
A isDeleted() 0 3 1
A setPaymentProviderDetailsUrl() 0 3 1
A setPublic() 0 3 1
A getTierComponents() 0 3 1
A getUsage() 0 3 1
A getProduct() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Price often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Price, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright (C) 2020-2025 Iain Cambridge
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
10
 * the Free Software Foundation, either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
namespace Parthenon\Billing\Entity;
23
24
use Brick\Money\Currency;
25
use Brick\Money\Money;
26
use Doctrine\Common\Collections\Collection;
27
use Parthenon\Athena\Entity\CrudEntityInterface;
28
use Parthenon\Athena\Entity\DeletableInterface;
29
use Parthenon\Billing\Enum\PriceType;
30
31
class Price implements CrudEntityInterface, DeletableInterface, PriceInterface
32
{
33
    protected $id;
34
35
    protected ?int $amount;
36
37
    protected string $currency;
38
39
    protected bool $recurring;
40
41
    protected ?string $schedule = null;
42
43
    protected ?string $externalReference = null;
44
45
    protected bool $includingTax = true;
46
47
    protected Product $product;
48
49
    protected ?bool $public = true;
50
51
    protected ?bool $isDeleted = false;
52
53
    protected \DateTimeInterface $createdAt;
54
55
    protected ?\DateTimeInterface $deletedAt = null;
56
57
    protected ?string $paymentProviderDetailsUrl = null;
58
59
    protected array|Collection $tierComponents = [];
60
61
    protected PriceType $type;
62
63
    private ?int $units = null;
64
65
    private ?bool $usage = false;
66
67
    public function getId()
68
    {
69
        return $this->id;
70
    }
71
72
    public function setId($id): void
73
    {
74
        $this->id = $id;
75
    }
76
77
    public function getAmount(): ?int
78
    {
79
        return $this->amount;
80
    }
81
82
    public function setAmount(?int $amount): void
83
    {
84
        $this->amount = $amount;
85
    }
86
87
    public function getCurrency(): string
88
    {
89
        return strtoupper($this->currency);
90
    }
91
92
    public function setCurrency(string $currency): void
93
    {
94
        $this->currency = $currency;
95
    }
96
97
    public function getExternalReference(): ?string
98
    {
99
        return $this->externalReference;
100
    }
101
102
    public function setExternalReference(?string $externalReference): void
103
    {
104
        $this->externalReference = $externalReference;
105
    }
106
107
    public function hasExternalReference(): bool
108
    {
109
        return isset($this->externalReference);
110
    }
111
112
    public function isRecurring(): bool
113
    {
114
        return $this->recurring;
115
    }
116
117
    public function setRecurring(bool $recurring): void
118
    {
119
        $this->recurring = $recurring;
120
    }
121
122
    public function getSchedule(): ?string
123
    {
124
        return $this->schedule;
125
    }
126
127
    public function setSchedule(?string $schedule): void
128
    {
129
        $this->schedule = $schedule;
130
    }
131
132
    public function isIncludingTax(): bool
133
    {
134
        return $this->includingTax;
135
    }
136
137
    public function setIncludingTax(bool $includingTax): void
138
    {
139
        $this->includingTax = $includingTax;
140
    }
141
142
    public function getAsMoney(): Money
143
    {
144
        if (!$this->amount) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->amount of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
145
            return Money::zero(Currency::of($this->currency));
146
        }
147
148
        return Money::ofMinor($this->amount, Currency::of($this->currency));
149
    }
150
151
    public function getProduct(): Product
152
    {
153
        return $this->product;
154
    }
155
156
    public function setProduct(Product $product): void
157
    {
158
        $this->product = $product;
159
    }
160
161
    public function isPublic(): bool
162
    {
163
        return true === $this->public;
164
    }
165
166
    public function setPublic(?bool $public): void
167
    {
168
        $this->public = $public;
169
    }
170
171
    public function getPaymentProviderDetailsUrl(): ?string
172
    {
173
        return $this->paymentProviderDetailsUrl;
174
    }
175
176
    public function setPaymentProviderDetailsUrl(?string $paymentProviderDetailsUrl): void
177
    {
178
        $this->paymentProviderDetailsUrl = $paymentProviderDetailsUrl;
179
    }
180
181
    public function getDisplayName(): string
182
    {
183
        if ($this->recurring) {
184
            $type = 'EmbeddedSubscription - '.$this->schedule;
185
        } else {
186
            $type = 'one-off';
187
        }
188
189
        return (string) $this->getAsMoney().' - '.$type.' - '.$this->getProduct()?->getName();
190
    }
191
192
    public function setDeletedAt(\DateTimeInterface $dateTime): DeletableInterface
193
    {
194
        $this->deletedAt = $dateTime;
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Parthenon\Athena\Entity\DeletableInterface. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
195
    }
196
197
    public function isDeleted(): bool
198
    {
199
        return true === $this->isDeleted;
200
    }
201
202
    public function markAsDeleted(): DeletableInterface
203
    {
204
        $this->deletedAt = new \DateTime('now');
205
        $this->isDeleted = true;
206
207
        return $this;
208
    }
209
210
    public function unmarkAsDeleted(): DeletableInterface
211
    {
212
        $this->deletedAt = null;
213
        $this->isDeleted = false;
214
215
        return $this;
216
    }
217
218
    public function getIsDeleted(): ?bool
219
    {
220
        return $this->isDeleted;
221
    }
222
223
    public function setIsDeleted(?bool $isDeleted): void
224
    {
225
        $this->isDeleted = $isDeleted;
226
    }
227
228
    public function getCreatedAt(): \DateTimeInterface
229
    {
230
        return $this->createdAt;
231
    }
232
233
    public function setCreatedAt(\DateTimeInterface $createdAt): void
234
    {
235
        $this->createdAt = $createdAt;
236
    }
237
238
    public function getTierComponents(): Collection|array
239
    {
240
        return $this->tierComponents;
241
    }
242
243
    public function setTierComponents(Collection|array $tierComponents): void
244
    {
245
        $this->tierComponents = $tierComponents;
246
    }
247
248
    public function getType(): PriceType
249
    {
250
        return $this->type;
251
    }
252
253
    public function setType(PriceType $type): void
254
    {
255
        $this->type = $type;
256
    }
257
258
    public function getUnits(): ?int
259
    {
260
        return $this->units;
261
    }
262
263
    public function setUnits(?int $units): void
264
    {
265
        $this->units = $units;
266
    }
267
268
    public function getUsage(): bool
269
    {
270
        return true === $this->usage;
271
    }
272
273
    public function setUsage(?bool $usage): void
274
    {
275
        $this->usage = $usage;
276
    }
277
}
278