Passed
Push — master ( 1b7f15...45b9b9 )
by Laurens
57s queued 11s
created

Passbook   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 415
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Test Coverage

Coverage 95.3%

Importance

Changes 0
Metric Value
wmc 58
lcom 2
cbo 5
dl 0
loc 415
ccs 142
cts 149
cp 0.953
rs 4.5599
c 0
b 0
f 0

31 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A setOrganizationName() 0 4 1
A setDescription() 0 4 1
A setPassTypeIdentifier() 0 4 1
A setTeamIdentifier() 0 4 1
A setLogoText() 0 4 1
A setRelevantDate() 0 4 1
A setBarcode() 0 4 1
A addLocation() 0 4 1
A setMaxDistance() 0 4 1
A setWebService() 0 5 1
A setForegroundColor() 0 4 1
A setBackgroundColor() 0 4 1
A setLabelColor() 0 4 1
A addImage() 0 4 1
A addHeaderField() 0 4 1
A addPrimaryField() 0 4 1
A addAuxiliaryField() 0 4 1
A addSecondaryField() 0 4 1
A addBackField() 0 4 1
A setAppLaunchURL() 0 4 1
A addAssociatedStoreIdentifiers() 0 4 1
A setUserInfo() 0 4 1
A voided() 0 4 1
A hasPassTypeIdentifier() 0 4 1
A hasTeamIdentifier() 0 4 1
A getData() 0 9 1
F getGenericData() 0 76 18
B getFieldsData() 0 22 6
A getImages() 0 4 1
B validate() 0 22 6

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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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\Field\Field;
12
use LauLamanApps\ApplePassbook\MetaData\Image;
13
use LauLamanApps\ApplePassbook\MetaData\Image\LocalImage;
14
use LauLamanApps\ApplePassbook\MetaData\Location;
15
use LauLamanApps\ApplePassbook\Style\Color;
16
use LogicException;
17
use Ramsey\Uuid\UuidInterface;
18
19
abstract class Passbook
20
{
21
    protected const TYPE = null;
22
23
    /**
24
     * @var int
25
     */
26
    private $formatVersion = 1;
27
28
    /**
29
     * @var string
30
     */
31
    private $passTypeIdentifier;
32
33
    /**
34
     * @var string
35
     */
36
    private $serialNumber;
37
38
    /**
39
     * @var string
40
     */
41
    private $teamIdentifier;
42
43
    /**
44
     * @var string
45
     */
46
    private $organizationName;
47
48
    /**
49
     * @var string
50
     */
51
    private $description;
52
53
    /**
54
     * @var string|null
55
     */
56
    private $logoText;
57
58
    /**
59
     * @var Barcode[]
60
     */
61
    private $barcodes = [];
62
63
    /**
64
     * @var DateTimeImmutable|null
65
     */
66
    private $relevantDate;
67
68
    /**
69
     * @var string|null
70
     */
71
    private $appLaunchURL;
72
73
    /**
74
     * @var array|null
75
     */
76
    private $associatedStoreIdentifiers;
77
78
    /**
79
     * @var string|null
80
     */
81
    private $userInfo;
82
83
    /**
84
     * @var DateTimeImmutable|null
85
     */
86
    private $expirationDate;
87
88
    /**
89
     * @var boolean|null
90
     */
91
    private $voided;
92
93
    /**
94
     * @var Location[]|null
95
     */
96
    private $locations;
97
98
    /**
99
     * @var int|null
100
     */
101
    private $maxDistance;
102
103
    /**
104
     * @var string|null
105
     */
106
    private $webServiceURL;
107
108
    /**
109
     * @var string|null
110
     */
111
    private $authenticationToken;
112
113
    /**
114
     * @var Color|null
115
     */
116
    private $foregroundColor;
117
118
    /**
119
     * @var Color|null
120
     */
121
    private $backgroundColor;
122
123
    /**
124
     * @var Color|null
125
     */
126
    private $labelColor;
127
128
    /**
129
     * @var LocalImage[]
130
     */
131
    private $images = [];
132
133
    /**
134
     * @var Field[]
135
     */
136
    private $headerFields = [];
137
138
    /**
139
     * @var Field[]
140
     */
141
    private $primaryFields = [];
142
143
    /**
144
     * @var Field[]
145
     */
146
    private $auxiliaryFields = [];
147
148
    /**
149
     * @var Field[]
150
     */
151
    private $secondaryFields = [];
152
153
    /**
154
     * @var Field[]
155
     */
156
    private $backFields = [];
157
158 3
    public function __construct(string $serialNumber)
159
    {
160 3
        $this->serialNumber = $serialNumber;
161 3
    }
162
163 3
    public function setOrganizationName(string $organizationName): void
164
    {
165 3
        $this->organizationName = $organizationName;
166 3
    }
167
168 3
    public function setDescription(string $description): void
169
    {
170 3
        $this->description = $description;
171 3
    }
172
173 3
    public function setPassTypeIdentifier(string $passTypeIdentifier): void
174
    {
175 3
        $this->passTypeIdentifier = $passTypeIdentifier;
176 3
    }
177
178 3
    public function setTeamIdentifier(string $teamIdentifier): void
179
    {
180 3
        $this->teamIdentifier = $teamIdentifier;
181 3
    }
182
183 7
    public function setLogoText(string $logoText): void
184
    {
185 7
        $this->logoText = $logoText;
186 7
    }
187
188 7
    public function setRelevantDate(DateTimeImmutable $relevantDate): void
189
    {
190 7
        $this->relevantDate = $relevantDate;
191 7
    }
192
193 7
    public function setBarcode(Barcode $barcode): void
194
    {
195 7
        $this->barcodes[] = $barcode;
196 7
    }
197
198 7
    public function addLocation(Location $location): void
199
    {
200 7
        $this->locations[] = $location;
201 7
    }
202
203 6
    public function setMaxDistance(int $maxDistance): void
204
    {
205 6
        $this->maxDistance = $maxDistance;
206 6
    }
207
208 7
    public function setWebService($url, $authenticationToken): void
209
    {
210 7
        $this->webServiceURL = $url;
211 7
        $this->authenticationToken = $authenticationToken;
212 7
    }
213
214 7
    public function setForegroundColor(Color $foregroundColor): void
215
    {
216 7
        $this->foregroundColor = $foregroundColor;
217 7
    }
218
219 7
    public function setBackgroundColor(Color $backgroundColor): void
220
    {
221 7
        $this->backgroundColor = $backgroundColor;
222 7
    }
223
224 6
    public function setLabelColor(Color $labelColor): void
225
    {
226 6
        $this->labelColor = $labelColor;
227 6
    }
228
229 6
    public function addImage(Image $image): void
230
    {
231 6
        $this->images[] = $image;
232 6
    }
233
234 7
    public function addHeaderField(Field $field): void
235
    {
236 7
        $this->headerFields[] = $field;
237 7
    }
238
239 7
    public function addPrimaryField(Field $field): void
240
    {
241 7
        $this->primaryFields[] = $field;
242 7
    }
243
244 1
    public function addAuxiliaryField(Field $field): void
245
    {
246 1
        $this->auxiliaryFields[] = $field;
247 1
    }
248
249 7
    public function addSecondaryField(Field $field): void
250
    {
251 7
        $this->secondaryFields[] = $field;
252 7
    }
253
254 7
    public function addBackField(Field $field): void
255
    {
256 7
        $this->backFields[] = $field;
257 7
    }
258
259 1
    public function setAppLaunchURL(string $appLaunchURL): void
260
    {
261 1
        $this->appLaunchURL = $appLaunchURL;
262 1
    }
263
264 1
    public function addAssociatedStoreIdentifiers(int $associatedStoreIdentifiers): void
265
    {
266 1
        $this->associatedStoreIdentifiers[] = $associatedStoreIdentifiers;
267 1
    }
268
269 1
    public function setUserInfo(string $userInfo): void
270
    {
271 1
        $this->userInfo = $userInfo;
272 1
    }
273
274 6
    public function voided(): void
275
    {
276 6
        $this->voided = true;
277 6
    }
278
279 6
    public function hasPassTypeIdentifier(): bool
280
    {
281 6
        return $this->passTypeIdentifier !== null;
282
    }
283
284 6
    public function hasTeamIdentifier(): bool
285
    {
286 6
        return $this->teamIdentifier !== null;
287
    }
288
289 3
    public function getData(): array
290
    {
291 3
        $this->validate();
292
293 2
        $data = $this->getGenericData();
294 2
        $data[static::TYPE] = $this->getFieldsData();
295
296 2
        return $data;
297
    }
298
299 2
    private function getGenericData(): array
300
    {
301
        $data = [
302 2
            'formatVersion' => $this->formatVersion,
303 2
            'passTypeIdentifier' => $this->passTypeIdentifier,
304 2
            'serialNumber' => $this->serialNumber,
305 2
            'teamIdentifier' => $this->teamIdentifier,
306 2
            'organizationName' => $this->organizationName,
307 2
            'description' => $this->description,
308
        ];
309
310 2
        if ($this->logoText !== null) {
311 1
            $data['logoText'] = $this->logoText;
312
        }
313
314 2
        if (count($this->barcodes) > 0) {
315 1
            $data['barcode'] = $this->barcodes[0]->toArray();
316
317 1
            foreach ($this->barcodes as $barcode) {
318 1
                $data['barcodes'][] = $barcode->toArray();
319
            }
320
        }
321
322 2
        if ($this->relevantDate !== null) {
323 1
            $data['relevantDate'] = $this->relevantDate->format(DateTimeInterface::W3C);
324
        }
325
326 2
        if ($this->expirationDate !== null) {
327
            $data['expirationDate'] = $this->expirationDate->format(DateTimeInterface::W3C);
328
        }
329
330 2
        if ($this->appLaunchURL !== null) {
331
            $data['appLaunchURL'] = $this->appLaunchURL;
332
        }
333
334 2
        if ($this->associatedStoreIdentifiers !== null) {
335
            $data['associatedStoreIdentifiers'] = $this->associatedStoreIdentifiers;
336
        }
337
338 2
        if ($this->userInfo !== null) {
339
            $data['userInfo'] = $this->userInfo;
340
        }
341
342 2
        if ($this->voided === true) {
343
            $data['voided'] = true;
344
        }
345
346 2
        if ($this->locations !== null) {
347 1
            foreach ($this->locations as $location) {
348 1
                $data['locations'][] = $location->toArray();
349
            }
350
        }
351
352 2
        if ($this->maxDistance !== null) {
353
            $data['maxDistance'] = $this->maxDistance;
354
        }
355
356 2
        if ($this->webServiceURL && $this->authenticationToken) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->webServiceURL of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $this->authenticationToken of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
357 1
            $data['webServiceURL'] = $this->webServiceURL;
358 1
            $data['authenticationToken'] = $this->authenticationToken;
359
        }
360
361 2
        if ($this->foregroundColor !== null) {
362 1
            $data['foregroundColor'] = $this->foregroundColor->toString();
363
        }
364
365 2
        if ($this->backgroundColor !== null) {
366 1
            $data['backgroundColor'] = $this->backgroundColor->toString();
367
        }
368
369 2
        if ($this->labelColor !== null) {
370
            $data['labelColor'] = $this->labelColor->toString();
371
        }
372
373 2
        return $data;
374
    }
375
376 2
    private function getFieldsData(): array
377
    {
378 2
        $data = [];
379
380 2
        foreach ($this->headerFields as $field) {
381 1
            $data['headerFields'][] = $field->getMetaData();
382
        }
383 2
        foreach ($this->primaryFields as $field) {
384 1
            $data['primaryFields'][] = $field->getMetaData();
385
        }
386 2
        foreach ($this->auxiliaryFields as $field) {
387 1
            $data['auxiliaryFields'][] = $field->getMetaData();
388
        }
389 2
        foreach ($this->secondaryFields as $field) {
390 1
            $data['secondaryFields'][] = $field->getMetaData();
391
        }
392 2
        foreach ($this->backFields as $field) {
393 1
            $data['backFields'][] = $field->getMetaData();
394
        }
395
396 2
        return $data;
397
    }
398
399
    /**
400
     * @return Image[]
401
     */
402 6
    public function getImages(): array
403
    {
404 6
        return $this->images;
405
    }
406
407
    /**
408
     * @throws LogicException
409
     * @throws MissingRequiredDataException
410
     */
411 29
    public function validate(): void
412
    {
413 29
        if (static::TYPE === null) {
414 1
            throw new LogicException('Please implement protected const TYPE in class.');
415
        }
416
417 28
        if ($this->passTypeIdentifier === null) {
418 5
            throw new MissingRequiredDataException('Please specify the PassTypeIdentifier before requesting the manifest data.');
419
        }
420
421 23
        if ($this->teamIdentifier === null) {
422 5
            throw new MissingRequiredDataException('Please specify the TeamIdentifier before requesting the manifest data.');
423
        }
424
425 18
        if ($this->organizationName === null) {
426 5
            throw new MissingRequiredDataException('Please specify the OrganizationName before requesting the manifest data.');
427
        }
428
429 13
        if ($this->description === null) {
430 5
            throw new MissingRequiredDataException('Please specify the Description before requesting the manifest data.');
431
        }
432 8
    }
433
}
434