Test Failed
Push — master ( e5d517...cfa2aa )
by Laurens
01:33
created

Passbook   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 373
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Test Coverage

Coverage 97.01%

Importance

Changes 0
Metric Value
wmc 52
lcom 2
cbo 5
dl 0
loc 373
ccs 130
cts 134
cp 0.9701
rs 7.44
c 0
b 0
f 0

28 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 voided() 0 4 1
A hasPassTypeIdentifier() 0 4 1
A hasTeamIdentifier() 0 4 1
A getData() 0 9 1
F getGenericData() 0 64 15
A getImages() 0 4 1
B validate() 0 22 6
B getFieldsData() 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 DateTimeImmutable|null
70
     */
71
    private $expirationDate;
72
73
    /**
74
     * @var boolean|null
75
     */
76
    private $voided;
77
78
    /**
79
     * @var Location[]|null
80
     */
81
    private $locations;
82
83
    /**
84
     * @var int|null
85
     */
86
    private $maxDistance;
87
88
    /**
89
     * @var string|null
90
     */
91
    private $webServiceURL;
92
93
    /**
94
     * @var string|null
95
     */
96
    private $authenticationToken;
97
98
    /**
99
     * @var Color|null
100
     */
101
    private $foregroundColor;
102
103
    /**
104
     * @var Color|null
105
     */
106
    private $backgroundColor;
107
108
    /**
109
     * @var Color|null
110
     */
111
    private $labelColor;
112
113
    /**
114
     * @var LocalImage[]
115
     */
116
    private $images = [];
117
118
    /**
119
     * @var Field[]
120
     */
121
    private $headerFields = [];
122
123
    /**
124
     * @var Field[]
125
     */
126
    private $primaryFields = [];
127
128
    /**
129
     * @var Field[]
130
     */
131
    private $auxiliaryFields = [];
132
133
    /**
134
     * @var Field[]
135
     */
136
    private $secondaryFields = [];
137
138
    /**
139
     * @var Field[]
140
     */
141
    private $backFields = [];
142
143 3
    public function __construct(string $serialNumber)
144
    {
145 3
        $this->serialNumber = $serialNumber;
146 3
    }
147
148 3
    public function setOrganizationName(string $organizationName): void
149
    {
150 3
        $this->organizationName = $organizationName;
151 3
    }
152
153 3
    public function setDescription(string $description): void
154
    {
155 3
        $this->description = $description;
156 3
    }
157
158 3
    public function setPassTypeIdentifier(string $passTypeIdentifier): void
159
    {
160 3
        $this->passTypeIdentifier = $passTypeIdentifier;
161 3
    }
162
163 3
    public function setTeamIdentifier(string $teamIdentifier): void
164
    {
165 3
        $this->teamIdentifier = $teamIdentifier;
166 3
    }
167
168 7
    public function setLogoText(string $logoText): void
169
    {
170 7
        $this->logoText = $logoText;
171 7
    }
172
173 7
    public function setRelevantDate(DateTimeImmutable $relevantDate): void
174
    {
175 7
        $this->relevantDate = $relevantDate;
176 7
    }
177
178 7
    public function setBarcode(Barcode $barcode): void
179
    {
180 7
        $this->barcodes[] = $barcode;
181 7
    }
182
183 7
    public function addLocation(Location $location): void
184
    {
185 7
        $this->locations[] = $location;
186 7
    }
187
188 6
    public function setMaxDistance(int $maxDistance): void
189
    {
190 6
        $this->maxDistance = $maxDistance;
191 6
    }
192
193 7
    public function setWebService($url, $authenticationToken): void
194
    {
195 7
        $this->webServiceURL = $url;
196 7
        $this->authenticationToken = $authenticationToken;
197 7
    }
198
199 7
    public function setForegroundColor(Color $foregroundColor): void
200
    {
201 7
        $this->foregroundColor = $foregroundColor;
202 7
    }
203
204 7
    public function setBackgroundColor(Color $backgroundColor): void
205
    {
206 7
        $this->backgroundColor = $backgroundColor;
207 7
    }
208
209 6
    public function setLabelColor(Color $labelColor): void
210
    {
211 6
        $this->labelColor = $labelColor;
212 6
    }
213
214 6
    public function addImage(Image $image): void
215
    {
216 6
        $this->images[] = $image;
217 6
    }
218
219 7
    public function addHeaderField(Field $field): void
220
    {
221 7
        $this->headerFields[] = $field;
222 7
    }
223
224 7
    public function addPrimaryField(Field $field): void
225
    {
226 7
        $this->primaryFields[] = $field;
227 7
    }
228
229 1
    public function addAuxiliaryField(Field $field): void
230
    {
231 1
        $this->auxiliaryFields[] = $field;
232 1
    }
233
234 7
    public function addSecondaryField(Field $field): void
235
    {
236 7
        $this->secondaryFields[] = $field;
237 7
    }
238
239 7
    public function addBackField(Field $field): void
240
    {
241 7
        $this->backFields[] = $field;
242 7
    }
243
244 6
    public function voided(): void
245
    {
246 6
        $this->voided = true;
247 6
    }
248
249 6
    public function hasPassTypeIdentifier(): bool
250
    {
251 6
        return $this->passTypeIdentifier !== null;
252
    }
253
254 6
    public function hasTeamIdentifier(): bool
255
    {
256 6
        return $this->teamIdentifier !== null;
257
    }
258
259 3
    public function getData(): array
260
    {
261 3
        $this->validate();
262
263 2
        $data = $this->getGenericData();
264 2
        $data[static::TYPE] = $this->getFieldsData();
265
266 2
        return $data;
267
    }
268
269 2
    private function getGenericData(): array
270
    {
271
        $data = [
272 2
            'formatVersion' => $this->formatVersion,
273 2
            'passTypeIdentifier' => $this->passTypeIdentifier,
274 2
            'serialNumber' => $this->serialNumber,
275 2
            'teamIdentifier' => $this->teamIdentifier,
276 2
            'organizationName' => $this->organizationName,
277 2
            'description' => $this->description,
278
        ];
279
280 2
        if ($this->logoText !== null) {
281 1
            $data['logoText'] = $this->logoText;
282
        }
283
284 2
        if (count($this->barcodes) > 0) {
285 1
            $data['barcode'] = $this->barcodes[0]->toArray();
286
287 1
            foreach ($this->barcodes as $barcode) {
288 1
                $data['barcodes'][] = $barcode->toArray();
289
            }
290
        }
291
292 2
        if ($this->relevantDate !== null) {
293 1
            $data['relevantDate'] = $this->relevantDate->format(DateTimeInterface::W3C);
294
        }
295
296 2
        if ($this->expirationDate !== null) {
297
            $data['expirationDate'] = $this->expirationDate->format(DateTimeInterface::W3C);
298
        }
299
300 2
        if ($this->voided === true) {
301
            $data['voided'] = true;
302
        }
303
304 2
        if ($this->locations !== null) {
305 1
            foreach ($this->locations as $location) {
306 1
                $data['locations'][] = $location->toArray();
307
            }
308
        }
309
310 2
        if ($this->maxDistance !== null) {
311
            $data['maxDistance'] = $this->maxDistance;
312
        }
313
314 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...
315 1
            $data['webServiceURL'] = $this->webServiceURL;
316 1
            $data['authenticationToken'] = $this->authenticationToken;
317
        }
318
319 2
        if ($this->foregroundColor !== null) {
320 1
            $data['foregroundColor'] = $this->foregroundColor->toString();
321
        }
322
323 2
        if ($this->backgroundColor !== null) {
324 1
            $data['backgroundColor'] = $this->backgroundColor->toString();
325
        }
326
327 2
        if ($this->labelColor !== null) {
328
            $data['labelColor'] = $this->labelColor->toString();
329
        }
330
331 2
        return $data;
332
    }
333
334 2
    private function getFieldsData(): array
335
    {
336 2
        $data = [];
337
338 2
        foreach ($this->headerFields as $field) {
339 1
            $data['headerFields'][] = $field->getMetaData();
340
        }
341 2
        foreach ($this->primaryFields as $field) {
342 1
            $data['primaryFields'][] = $field->getMetaData();
343
        }
344 2
        foreach ($this->auxiliaryFields as $field) {
345 1
            $data['auxiliaryFields'][] = $field->getMetaData();
346
        }
347 2
        foreach ($this->secondaryFields as $field) {
348 1
            $data['secondaryFields'][] = $field->getMetaData();
349
        }
350 2
        foreach ($this->backFields as $field) {
351 1
            $data['backFields'][] = $field->getMetaData();
352
        }
353
354 2
        return $data;
355
    }
356
357
    /**
358
     * @return Image[]
359
     */
360 6
    public function getImages(): array
361
    {
362 6
        return $this->images;
363
    }
364
365
    /**
366
     * @throws LogicException
367
     * @throws MissingRequiredDataException
368
     */
369 29
    public function validate(): void
370
    {
371 29
        if (static::TYPE === null) {
372 1
            throw new LogicException('Please implement protected const TYPE in class.');
373
        }
374
375 28
        if ($this->passTypeIdentifier === null) {
376 5
            throw new MissingRequiredDataException('Please specify the PassTypeIdentifier before requesting the manifest data.');
377
        }
378
379 23
        if ($this->teamIdentifier === null) {
380 5
            throw new MissingRequiredDataException('Please specify the TeamIdentifier before requesting the manifest data.');
381
        }
382
383 18
        if ($this->organizationName === null) {
384 5
            throw new MissingRequiredDataException('Please specify the OrganizationName before requesting the manifest data.');
385
        }
386
387 13
        if ($this->description === null) {
388 5
            throw new MissingRequiredDataException('Please specify the Description before requesting the manifest data.');
389
        }
390 8
    }
391
}
392