Passbook   F
last analyzed

Complexity

Total Complexity 74

Size/Duplication

Total Lines 403
Duplicated Lines 0 %

Test Coverage

Coverage 95.14%

Importance

Changes 5
Bugs 1 Features 0
Metric Value
eloc 152
c 5
b 1
f 0
dl 0
loc 403
ccs 137
cts 144
cp 0.9514
rs 2.48
wmc 74

39 Methods

Rating   Name   Duplication   Size   Complexity  
A setDescription() 0 3 1
A getData() 0 8 1
A setGroupingIdentifier() 0 3 1
A addHeaderField() 0 3 1
A setNfc() 0 6 2
A hasPassTypeIdentifier() 0 3 1
A addSemanticTag() 0 3 1
A setPassTypeIdentifier() 0 3 1
A setMaxDistance() 0 3 1
A validate() 0 20 6
A addBackField() 0 3 1
A setLabelColor() 0 3 1
A setBarcode() 0 3 1
A addSecondaryField() 0 3 1
A addAuxiliaryField() 0 3 1
A setBackgroundColor() 0 3 1
A getFieldsData() 0 21 6
A setWebService() 0 4 1
A suppressStripShine() 0 3 1
A addLocation() 0 3 1
A setExpirationDate() 0 3 1
A hasTeamIdentifier() 0 3 1
A setForegroundColor() 0 3 1
A voided() 0 3 1
A setRelevantDate() 0 3 1
A prohibitSharing() 0 3 1
A setOrganizationName() 0 3 1
A setTeamIdentifier() 0 3 1
A setLogoText() 0 3 1
A addAssociatedStoreIdentifiers() 0 3 1
A setUserInfo() 0 3 1
A addBeacon() 0 3 1
A setAppLaunchURL() 0 3 1
A getImages() 0 3 1
A addPrimaryField() 0 3 1
A isVoided() 0 3 1
A __construct() 0 3 1
A addImage() 0 3 1
F getGenericData() 0 101 25

How to fix   Complexity   

Complex Class

Complex classes like Passbook 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 Passbook, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace LauLamanApps\ApplePassbook;
6
7
use DateTimeImmutable;
8
use DateTimeInterface;
9
use LauLamanApps\ApplePassbook\Exception\MissingRequiredDataException;
10
use LauLamanApps\ApplePassbook\MetaData\Barcode;
11
use LauLamanApps\ApplePassbook\MetaData\Beacon;
12
use LauLamanApps\ApplePassbook\MetaData\Field\Field;
13
use LauLamanApps\ApplePassbook\MetaData\Image;
14
use LauLamanApps\ApplePassbook\MetaData\Location;
15
use LauLamanApps\ApplePassbook\MetaData\Nfc;
16
use LauLamanApps\ApplePassbook\MetaData\SemanticTag;
17
use LauLamanApps\ApplePassbook\Style\Color;
18
use LogicException;
19
20
abstract class Passbook
21
{
22
    protected const TYPE = null;
23
24
    private string $appLaunchURL;
25
    /** @var int[] */
26
    private array $associatedStoreIdentifiers = [];
27
    private string $authenticationToken;
28
    /** @var Field[] */
29
    private array $auxiliaryFields = [];
30
    /** @var Field[] */
31
    private array $backFields = [];
32
    private Color $backgroundColor;
33
    /** @var Barcode[] */
34
    private array $barcodes = [];
35
    /** @var Beacon[] */
36
    private array $beacons = [];
37
    private string $description;
38
    private DateTimeImmutable $expirationDate;
39
    private Color $foregroundColor;
40
    private int $formatVersion = 1;
41
    private string $groupingIdentifier;
42
    /** @var Field[] */
43
    private array $headerFields = [];
44
    /** @var Image[] */
45
    private array $images = [];
46
    private Color $labelColor;
47
    /** @var Location[] */
48
    private array $locations = [];
49
    private string $logoText;
50
    private int $maxDistance;
51
    private ?Nfc $nfc = null;
52
    private string $organizationName;
53
    private string $passTypeIdentifier;
54
    /** @var Field[] */
55
    private array $primaryFields = [];
56
    private DateTimeImmutable $relevantDate;
57
    /** @var Field[] */
58
    private array $secondaryFields = [];
59
    /** @var SemanticTag[] */
60
    private array $semantics;
61
    private string $serialNumber;
62
    private bool $sharingProhibited = false;
63
    private bool $suppressStripShine = false;
64
    private string $teamIdentifier;
65
    private string $userInfo;
66
    private bool $voided = false;
67
    private string $webServiceURL;
68
69
    public function __construct(string $serialNumber)
70
    {
71
        $this->serialNumber = $serialNumber;
72
    }
73
74
    public function setOrganizationName(string $organizationName): void
75
    {
76
        $this->organizationName = $organizationName;
77
    }
78
79
    public function setDescription(string $description): void
80
    {
81
        $this->description = $description;
82
    }
83
84
    public function setPassTypeIdentifier(string $passTypeIdentifier): void
85
    {
86
        $this->passTypeIdentifier = $passTypeIdentifier;
87
    }
88
89
    public function setTeamIdentifier(string $teamIdentifier): void
90
    {
91
        $this->teamIdentifier = $teamIdentifier;
92
    }
93
94
    public function setLogoText(string $logoText): void
95
    {
96
        $this->logoText = $logoText;
97
    }
98
99
    public function setRelevantDate(DateTimeImmutable $relevantDate): void
100
    {
101
        $this->relevantDate = $relevantDate;
102
    }
103
104
    public function setExpirationDate(DateTimeImmutable $expirationDate): void
105
    {
106
        $this->expirationDate = $expirationDate;
107
    }
108
109
    public function setBarcode(Barcode $barcode): void
110
    {
111
        $this->barcodes[] = $barcode;
112
    }
113
114
    public function addBeacon(Beacon $beacon): void
115
    {
116
        $this->beacons[] = $beacon;
117
    }
118
119
    public function addLocation(Location $location): void
120
    {
121
        $this->locations[] = $location;
122
    }
123
124
    public function setMaxDistance(int $maxDistance): void
125
    {
126
        $this->maxDistance = $maxDistance;
127
    }
128
129
    public function setNfc(Nfc $nfc): void
130
    {
131
        $this->nfc = $nfc;
132
133
        if ($nfc->requiresAuthentication()) {
134
            $this->prohibitSharing();
135
        }
136
    }
137
138
    public function setWebService(string $url, string $authenticationToken): void
139
    {
140
        $this->webServiceURL = $url;
141
        $this->authenticationToken = $authenticationToken;
142
    }
143
144
    public function setForegroundColor(Color $foregroundColor): void
145
    {
146
        $this->foregroundColor = $foregroundColor;
147
    }
148
149
    public function setGroupingIdentifier(string $groupingIdentifier): void
150
    {
151
        $this->groupingIdentifier = $groupingIdentifier;
152
    }
153
154
    public function setBackgroundColor(Color $backgroundColor): void
155
    {
156
        $this->backgroundColor = $backgroundColor;
157
    }
158 3
159
    public function setLabelColor(Color $labelColor): void
160 3
    {
161 3
        $this->labelColor = $labelColor;
162
    }
163 3
164
    public function addImage(Image $image): void
165 3
    {
166 3
        $this->images[] = $image;
167
    }
168 3
169
    public function addHeaderField(Field $field): void
170 3
    {
171 3
        $this->headerFields[] = $field;
172
    }
173 3
174
    public function addPrimaryField(Field $field): void
175 3
    {
176 3
        $this->primaryFields[] = $field;
177
    }
178 3
179
    public function addAuxiliaryField(Field $field): void
180 3
    {
181 3
        $this->auxiliaryFields[] = $field;
182
    }
183 7
184
    public function addSecondaryField(Field $field): void
185 7
    {
186 7
        $this->secondaryFields[] = $field;
187
    }
188 7
189
    public function addBackField(Field $field): void
190 7
    {
191 7
        $this->backFields[] = $field;
192
    }
193 7
194
    public function setAppLaunchURL(string $appLaunchURL): void
195 7
    {
196 7
        $this->appLaunchURL = $appLaunchURL;
197
    }
198 7
199
    public function addAssociatedStoreIdentifiers(int $associatedStoreIdentifiers): void
200 7
    {
201 7
        $this->associatedStoreIdentifiers[] = $associatedStoreIdentifiers;
202
    }
203 6
204
    public function addSemanticTag(SemanticTag $semanticTag): void
205 6
    {
206 6
        $this->semantics[] = $semanticTag;
207
    }
208 7
209
    public function setUserInfo(string $userInfo): void
210 7
    {
211 7
        $this->userInfo = $userInfo;
212 7
    }
213
214 7
    public function voided(): void
215
    {
216 7
        $this->voided = true;
217 7
    }
218
219 7
    public function prohibitSharing(): void
220
    {
221 7
        $this->sharingProhibited = true;
222 7
    }
223
224 6
    public function suppressStripShine(): void
225
    {
226 6
        $this->suppressStripShine = true;
227 6
    }
228
229 6
    public function isVoided(): bool
230
    {
231 6
        return $this->voided;
232 6
    }
233
234 7
    public function hasPassTypeIdentifier(): bool
235
    {
236 7
        return isset($this->passTypeIdentifier);
237 7
    }
238
239 7
    public function hasTeamIdentifier(): bool
240
    {
241 7
        return isset($this->teamIdentifier);
242 7
    }
243
244 1
    /**
245
     * @throws MissingRequiredDataException
246 1
     * @return array<int|string, mixed>
247 1
     */
248
    public function getData(): array
249 7
    {
250
        $this->validate();
251 7
252 7
        $data = $this->getGenericData();
253
        $data[static::TYPE] = $this->getFieldsData();
254 7
255
        return $data;
256 7
    }
257 7
258
    /**
259 1
     * @throws LogicException
260
     * @throws MissingRequiredDataException
261 1
     */
262 1
    public function validate(): void
263
    {
264 1
        if (static::TYPE === null) {
0 ignored issues
show
introduced by
The condition static::TYPE === null is always true.
Loading history...
265
            throw new LogicException('Please implement protected const TYPE in class.');
266 1
        }
267 1
268
        if (!isset($this->passTypeIdentifier)) {
269 1
            throw new MissingRequiredDataException('Please specify the PassTypeIdentifier before requesting the manifest data.');
270
        }
271 1
272 1
        if (!isset($this->teamIdentifier)) {
273
            throw new MissingRequiredDataException('Please specify the TeamIdentifier before requesting the manifest data.');
274 6
        }
275
276 6
        if (!isset($this->organizationName)) {
277 6
            throw new MissingRequiredDataException('Please specify the OrganizationName before requesting the manifest data.');
278
        }
279 6
280
        if (!isset($this->description)) {
281 6
            throw new MissingRequiredDataException('Please specify the Description before requesting the manifest data.');
282
        }
283
    }
284 6
285
    /**
286 6
     * @return array<int|string, mixed>
287
     */
288
    private function getGenericData(): array
289 3
    {
290
        $data = [
291 3
            'formatVersion' => $this->formatVersion,
292
            'passTypeIdentifier' => $this->passTypeIdentifier,
293 2
            'serialNumber' => $this->serialNumber,
294 2
            'teamIdentifier' => $this->teamIdentifier,
295
            'organizationName' => $this->organizationName,
296 2
            'description' => $this->description,
297
        ];
298
299 2
        if (isset($this->logoText)) {
300
            $data['logoText'] = $this->logoText;
301
        }
302 2
303 2
        if (count($this->barcodes) > 0) {
304 2
            $data['barcode'] = $this->barcodes[0]->toArray();
305 2
306 2
            foreach ($this->barcodes as $barcode) {
307 2
                $data['barcodes'][] = $barcode->toArray();
308
            }
309
        }
310 2
311 1
        foreach ($this->beacons as $beacon) {
312
            $data['beacons'][] = $beacon->toArray();
313
        }
314 2
315 1
        if (isset($this->relevantDate)) {
316
            $data['relevantDate'] = $this->relevantDate->format(DateTimeInterface::W3C);
317 1
        }
318 1
319
        if (isset($this->expirationDate)) {
320
            $data['expirationDate'] = $this->expirationDate->format(DateTimeInterface::W3C);
321
        }
322 2
323 1
        if (isset($this->appLaunchURL)) {
324
            $data['appLaunchURL'] = $this->appLaunchURL;
325
        }
326 2
327
        if (count($this->associatedStoreIdentifiers) > 0) {
328
            $data['associatedStoreIdentifiers'] = $this->associatedStoreIdentifiers;
329
        }
330 2
331
        if (isset($this->userInfo)) {
332
            $data['userInfo'] = $this->userInfo;
333
        }
334 2
335
        if ($this->voided) {
336
            $data['voided'] = $this->voided;
337
        }
338 2
339
        if (isset($this->locations)) {
340
            foreach ($this->locations as $location) {
341
                $data['locations'][] = $location->toArray();
342 2
            }
343
        }
344
345
        if (isset($this->maxDistance)) {
346 2
            $data['maxDistance'] = $this->maxDistance;
347 1
        }
348 1
349
        if (isset($this->nfc)) {
350
            $data['nfc'] = $this->nfc->toArray();
351
        }
352 2
353
        if (isset($this->webServiceURL) && isset($this->authenticationToken)) {
354
            $data['webServiceURL'] = $this->webServiceURL;
355
            $data['authenticationToken'] = $this->authenticationToken;
356 2
        }
357 1
358 1
        if (isset($this->foregroundColor)) {
359
            $data['foregroundColor'] = $this->foregroundColor->toString();
360
        }
361 2
362 1
        if (isset($this->groupingIdentifier)) {
363
            $data['groupingIdentifier'] = $this->groupingIdentifier;
364
        }
365 2
366 1
        if (isset($this->backgroundColor)) {
367
            $data['backgroundColor'] = $this->backgroundColor->toString();
368
        }
369 2
370
        if (isset($this->labelColor)) {
371
            $data['labelColor'] = $this->labelColor->toString();
372
        }
373 2
374
        if ($this->sharingProhibited) {
375
            $data['sharingProhibited'] = $this->sharingProhibited;
376 2
        }
377
378 2
        if ($this->suppressStripShine) {
379
            $data['suppressStripShine'] = $this->suppressStripShine;
380 2
        }
381 1
382
        if (isset($this->semantics)) {
383 2
            foreach ($this->semantics as $tag) {
384 1
                $data['semantics'][$tag->getKey()] = $tag->getValue();
385
            }
386 2
        }
387 1
388
        return $data;
389 2
    }
390 1
391
    /**
392 2
     * @return array<int|string, mixed>
393 1
     */
394
    private function getFieldsData(): array
395
    {
396 2
        $data = [];
397
398
        foreach ($this->headerFields as $field) {
399
            $data['headerFields'][] = $field->getMetaData();
400
        }
401
        foreach ($this->primaryFields as $field) {
402 6
            $data['primaryFields'][] = $field->getMetaData();
403
        }
404 6
        foreach ($this->auxiliaryFields as $field) {
405
            $data['auxiliaryFields'][] = $field->getMetaData();
406
        }
407
        foreach ($this->secondaryFields as $field) {
408
            $data['secondaryFields'][] = $field->getMetaData();
409
        }
410
        foreach ($this->backFields as $field) {
411 29
            $data['backFields'][] = $field->getMetaData();
412
        }
413 29
414 1
        return $data;
415
    }
416
417 28
    /**
418 5
     * @return Image[]
419
     */
420
    public function getImages(): array
421 23
    {
422 5
        return $this->images;
423
    }
424
}
425