AppInfo   B
last analyzed

Complexity

Total Complexity 46

Size/Duplication

Total Lines 497
Duplicated Lines 0 %

Test Coverage

Coverage 21.48%

Importance

Changes 0
Metric Value
wmc 46
eloc 130
c 0
b 0
f 0
dl 0
loc 497
ccs 29
cts 135
cp 0.2148
rs 8.72

32 Methods

Rating   Name   Duplication   Size   Complexity  
A isAutoTranslatedDescription() 0 3 1
A getReleased() 0 3 1
A getContentRating() 0 3 1
A getAppVersion() 0 3 1
A getPrivacyPoliceUrl() 0 3 1
A getRecentChanges() 0 3 1
A getCategory() 0 3 1
A isContainsIAP() 0 3 1
A getSize() 0 3 1
A getCurrency() 0 3 1
A getSummary() 0 3 1
A getInstalls() 0 3 1
A getCategoryFamily() 0 3 1
A getAndroidVersion() 0 3 1
A isContainsAds() 0 3 1
A getNumberVoters() 0 3 1
B asArray() 0 35 8
A getMinAndroidVersion() 0 3 1
A getPrice() 0 3 1
A equals() 0 30 6
A isEditorsChoice() 0 3 1
A getTranslatedFromLocale() 0 3 1
A getVideo() 0 3 1
A getDeveloperName() 0 3 2
A getNumberReviews() 0 3 1
A getDeveloper() 0 3 1
A getReviews() 0 3 1
A __construct() 0 35 2
A getOffersIAPCost() 0 3 1
A getCover() 0 3 1
A getUpdated() 0 3 1
A getHistogramRating() 0 3 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright (c) Ne-Lexa
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 *
11
 * @see https://github.com/Ne-Lexa/google-play-scraper
12
 */
13
14
namespace Nelexa\GPlay\Model;
15
16
use Nelexa\GPlay\GPlayApps;
17
use Nelexa\GPlay\Model\Builder\AppBuilder;
18
19
/**
20
 * Contains detailed information about the application from the Google Play store.
21
 *
22
 * @see App Basic information about the application from the Google Play store.
23
 * @see GPlayApps::getAppInfo() Returns detailed information about the Android
24
 *     application from the Google Play store.
25
 * @see GPlayApps::getAppsInfo() Returns detailed information about many android packages.
26
 * @see GPlayApps::getAppInLocales() Returns detailed information about an application
27
 *     from the Google Play store for an array of locales.
28
 * @see GPlayApps::getAppInfoForAvailableLocales() Returns detailed information about the
29
 *     application in all available locales.
30
 */
31
final class AppInfo extends App
32
{
33
    /** @var string Default currency. */
34
    private const DEFAULT_CURRENCY = 'USD';
35
36
    /** @var string|null Application summary. */
37
    private $summary;
38
39
    /** @var GoogleImage|null Cover image. */
40
    private $cover;
41
42
    /** @var Category|null Application category. */
43
    private $category;
44
45
    /** @var Category|null Family category or null/ */
46
    private $categoryFamily;
47
48
    /** @var Video|null Promo video or null. */
49
    private $video;
50
51
    /** @var string|null Recent changes. */
52
    private $recentChanges;
53
54
    /** @var bool Editors' choice. */
55
    private $editorsChoice;
56
57
    /** @var int Number of application installations. */
58
    private $installs;
59
60
    /** @var HistogramRating Histogram rating. */
61
    private $histogramRating;
62
63
    /** @var float Price of the application in the Google Play store. */
64
    private $price;
65
66
    /** @var string Currency price of the application. */
67
    private $currency;
68
69
    /** @var string|null In-App Purchase price. */
70
    private $offersIAPCost;
71
72
    /** @var bool Application contains ads. */
73
    private $containsAds;
74
75
    /** @var string|null Application version, null if the application version depends on the device. */
76
    private $appVersion;
77
78
    /** @var string|null Android version, null if android version depends on the device. */
79
    private $androidVersion;
80
81
    /** @var string|null Minimum android version, null if android version depends on the device. */
82
    private $minAndroidVersion;
83
84
    /** @var string|null Content rating. */
85
    private $contentRating;
86
87
    /** @var string|null Privacy policy URL. */
88
    private $privacyPoliceUrl;
89
90
    /** @var \DateTimeInterface|null Release date if known. */
91
    private $released;
92
93
    /** @var \DateTimeInterface|null Update date or null. */
94
    private $updated;
95
96
    /** @var int Number of voters. */
97
    private $numberVoters;
98
99
    /** @var int Number of reviews. */
100
    private $numberReviews;
101
102
    /** @var Review[] Some useful reviews. */
103
    private $reviews;
104
105
    /** @var \Nelexa\GPlay\Model\Developer|null Application developer */
106
    private $developer;
107
108
    /**
109
     * Returns an object with detailed information about the application.
110
     *
111
     * @param AppBuilder $builder application builder
112
     *
113
     * @ignore
114
     */
115 10
    public function __construct(AppBuilder $builder)
116
    {
117 10
        parent::__construct($builder);
118
119 10
        if (empty($builder->getDescription())) {
120
            throw new \InvalidArgumentException(
121
                'Application description cannot be null or empty. Solution: $appBuilder->setDescription(...);'
122
            );
123
        }
124
125 10
        $this->summary = $builder->getSummary();
126 10
        $this->developer = $builder->getDeveloper();
127 10
        $this->cover = $builder->getCover();
128 10
        $this->category = $builder->getCategory();
129 10
        $this->categoryFamily = $builder->getCategoryFamily();
130 10
        $this->privacyPoliceUrl = $builder->getPrivacyPoliceUrl();
131 10
        $this->video = $builder->getVideo();
132 10
        $this->recentChanges = $builder->getRecentChanges();
133 10
        $this->editorsChoice = $builder->isEditorsChoice();
134 10
        $this->installs = $builder->getInstalls();
135 10
        $this->histogramRating = $builder->getHistogramRating()
136
            ?? new HistogramRating(0, 0, 0, 0, 0);
137 10
        $this->price = $builder->getPrice();
138 10
        $this->currency = $builder->getCurrency() ?? self::DEFAULT_CURRENCY;
139 10
        $this->offersIAPCost = $builder->getOffersIAPCost();
140 10
        $this->containsAds = $builder->isContainsAds();
141 10
        $this->appVersion = $builder->getAppVersion();
142 10
        $this->androidVersion = $builder->getAndroidVersion();
143 10
        $this->minAndroidVersion = $builder->getMinAndroidVersion();
144 10
        $this->contentRating = $builder->getContentRating();
145 10
        $this->released = $builder->getReleased();
146 10
        $this->updated = $builder->getUpdated();
147 10
        $this->numberVoters = $builder->getNumberVoters();
148 10
        $this->numberReviews = $builder->getNumberReviews();
149 10
        $this->reviews = $builder->getReviews();
150
    }
151
152
    /**
153
     * Returns application developer.
154
     *
155
     * @return \Nelexa\GPlay\Model\Developer|null application developer
156
     */
157 2
    public function getDeveloper(): ?Developer
158
    {
159 2
        return $this->developer;
160
    }
161
162
    public function getDeveloperName(): ?string
163
    {
164
        return $this->developer ? $this->developer->getName() : parent::getDeveloperName();
165
    }
166
167
    /**
168
     * Returns a summary of the application.
169
     *
170
     * @return string summary of the application
171
     */
172
    public function getSummary(): string
173
    {
174
        return $this->summary;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->summary could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
175
    }
176
177
    /**
178
     * @deprecated
179
     */
180
    public function isAutoTranslatedDescription(): bool
181
    {
182
        return false;
183
    }
184
185
    /**
186
     * @deprecated
187
     */
188
    public function getTranslatedFromLocale(): ?string
189
    {
190
        return null;
191
    }
192
193
    /**
194
     * Returns cover image.
195
     *
196
     * **Where it's displayed**
197
     * The feature graphic is displayed in front of screenshots of the application and in
198
     * the list of developer applications. If a promo video is added, a **Play** button
199
     * will overlay on the feature graphic so users can watch the promo video.
200
     *
201
     * Google Play requirements:
202
     * * JPEG or 24-bit PNG (no alpha)
203
     * * Dimensions: 1024px by 500px
204
     *
205
     * @return GoogleImage|null cover image or `null`
206
     *
207
     * @see https://support.google.com/googleplay/android-developer/answer/1078870?hl=en Graphic assets,
208
     *     screenshots, & video. Section **Feature graphic**.
209
     */
210
    public function getCover(): ?GoogleImage
211
    {
212
        return $this->cover;
213
    }
214
215
    /**
216
     * Returns the category of the application.
217
     *
218
     * @return Category|null category of application or `null`
219
     */
220
    public function getCategory(): ?Category
221
    {
222
        return $this->category;
223
    }
224
225
    /**
226
     * Returns family category.
227
     *
228
     * @return Category|null family category or `null`
229
     */
230
    public function getCategoryFamily(): ?Category
231
    {
232
        return $this->categoryFamily;
233
    }
234
235
    /**
236
     * Returns a video about the application.
237
     *
238
     * @return Video|null promo video or `null`
239
     */
240
    public function getVideo(): ?Video
241
    {
242
        return $this->video;
243
    }
244
245
    /**
246
     * Returns recent changes.
247
     *
248
     * @return string|null recent changes or null if not provided
249
     */
250
    public function getRecentChanges(): ?string
251
    {
252
        return $this->recentChanges;
253
    }
254
255
    /**
256
     * Checks if the application is an editors' choice.
257
     *
258
     * @return bool `true` if the application is selected by Google Play editor, otherwise `false`
259
     */
260
    public function isEditorsChoice(): bool
261
    {
262
        return $this->editorsChoice;
263
    }
264
265
    /**
266
     * Returns the number of installations of the application.
267
     *
268
     * @return int the number of installations of the application
269
     */
270
    public function getInstalls(): int
271
    {
272
        return $this->installs;
273
    }
274
275
    /**
276
     * Returns histogram rating.
277
     *
278
     * @return HistogramRating histogram rating
279
     */
280
    public function getHistogramRating(): HistogramRating
281
    {
282
        return $this->histogramRating;
283
    }
284
285
    /**
286
     * Returns the price of the app in the Google Play store.
287
     *
288
     * @return float price or 0.00 if the app is free
289
     *
290
     * @see AppInfo::getCurrency() Returns the price currency
291
     *     of the app in the Google Play store.
292
     */
293
    public function getPrice(): float
294
    {
295
        return $this->price;
296
    }
297
298
    /**
299
     * Returns the price currency of the app in the Google Play store.
300
     *
301
     * @return string currency price of the application, default USD
302
     */
303
    public function getCurrency(): string
304
    {
305
        return $this->currency;
306
    }
307
308
    /**
309
     * Checks if the app contains In-App Purchases (IAP).
310
     *
311
     * @return bool `true` if the application contains AIP, and `false` if not contains
312
     */
313
    public function isContainsIAP(): bool
314
    {
315
        return $this->offersIAPCost !== null;
316
    }
317
318
    /**
319
     * Returns the cost of In-App Purchases (IAP).
320
     *
321
     * @return string|null in-App Purchase price
322
     */
323
    public function getOffersIAPCost(): ?string
324
    {
325
        return $this->offersIAPCost;
326
    }
327
328
    /**
329
     * Checks if the app contains ads.
330
     *
331
     * @return bool `true` if the application contains ads, and `false` if not contains
332
     */
333
    public function isContainsAds(): bool
334
    {
335
        return $this->containsAds;
336
    }
337
338
    /**
339
     * @deprecated
340
     */
341
    public function getSize(): ?string
342
    {
343
        return null;
344
    }
345
346
    /**
347
     * Returns the version of the application.
348
     *
349
     * @return string|null application version, `null` if the application version depends on the device
350
     */
351
    public function getAppVersion(): ?string
352
    {
353
        return $this->appVersion;
354
    }
355
356
    /**
357
     * Returns the supported version of Android.
358
     *
359
     * @return string|null android version, `null` if android version depends on the device
360
     */
361
    public function getAndroidVersion(): ?string
362
    {
363
        return $this->androidVersion;
364
    }
365
366
    /**
367
     * Returns the minimum supported version of Android.
368
     *
369
     * @return string|null minimum android version, `null` if android version depends on the device
370
     */
371
    public function getMinAndroidVersion(): ?string
372
    {
373
        return $this->minAndroidVersion;
374
    }
375
376
    /**
377
     * Returns the age limit.
378
     *
379
     * @return string|null Content rating or `null` if not provided
380
     */
381
    public function getContentRating(): ?string
382
    {
383
        return $this->contentRating;
384
    }
385
386
    /**
387
     * Returns privacy policy URL.
388
     *
389
     * @return string|null privacy policy URL
390
     */
391
    public function getPrivacyPoliceUrl(): ?string
392
    {
393
        return $this->privacyPoliceUrl;
394
    }
395
396
    /**
397
     * Returns the release date.
398
     *
399
     * @return \DateTimeInterface|null release date or `null` if not provided
400
     */
401
    public function getReleased(): ?\DateTimeInterface
402
    {
403
        return $this->released;
404
    }
405
406
    /**
407
     * Returns the date of the update.
408
     *
409
     * @return \DateTimeInterface|null update date or `null` if not provided
410
     */
411
    public function getUpdated(): ?\DateTimeInterface
412
    {
413
        return $this->updated;
414
    }
415
416
    /**
417
     * Returns the number of voters.
418
     *
419
     * @return int number of voters
420
     */
421
    public function getNumberVoters(): int
422
    {
423
        return $this->numberVoters;
424
    }
425
426
    /**
427
     * Returns the number of reviews.
428
     *
429
     * @return int number of reviews
430
     */
431
    public function getNumberReviews(): int
432
    {
433
        return $this->numberReviews;
434
    }
435
436
    /**
437
     * Returns some useful reviews.
438
     *
439
     * @return Review[] some useful reviews
440
     */
441
    public function getReviews(): array
442
    {
443
        return $this->reviews;
444
    }
445
446
    /**
447
     * Checks for equality of applications.
448
     *
449
     * @param AppInfo $otherApp application with which is compared
450
     *
451
     * @return bool `true` if the contents of the objects being changed are the same
452
     *              and `false` if the objects contain different data
453
     *
454
     * @internal
455
     */
456
    public function equals(self $otherApp): bool
457
    {
458
        if ($otherApp->getId() !== $this->getId()) {
459
            return false;
460
        }
461
462
        if ($otherApp->getName() !== $this->getName()) {
463
            return false;
464
        }
465
466
        if ($otherApp->getDescription() !== $this->getDescription()) {
467
            return false;
468
        }
469
470
        if ($otherApp->recentChanges !== $this->recentChanges) {
471
            return false;
472
        }
473
474
        if ($otherApp->getIcon()->getOriginalSizeUrl() !== $this->getIcon()->getOriginalSizeUrl()) {
475
            return false;
476
        }
477
        $diff = array_udiff(
478
            $otherApp->getScreenshots(),
479
            $this->getScreenshots(),
480
            static function (GoogleImage $a, GoogleImage $b) {
481
                return strcmp($a->getOriginalSizeUrl(), $b->getOriginalSizeUrl());
482
            }
483
        );
484
485
        return empty($diff);
486
    }
487
488
    /**
489
     * Returns class properties as an array.
490
     *
491
     * @return array class properties as an array
492
     */
493
    public function asArray(): array
494
    {
495
        $array = parent::asArray();
496
        $array['cover'] = $this->cover !== null ? $this->cover->getUrl() : null;
497
        $array['category'] = $this->category->asArray();
0 ignored issues
show
Bug introduced by
The method asArray() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

497
        /** @scrutinizer ignore-call */ 
498
        $array['category'] = $this->category->asArray();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
498
        $array['categoryFamily'] = $this->categoryFamily !== null ? $this->categoryFamily->asArray() : null;
499
        $array['video'] = $this->video !== null ? $this->video->asArray() : null;
500
        $array['privacyPoliceUrl'] = $this->privacyPoliceUrl;
501
        $array['recentChange'] = $this->recentChanges;
502
        $array['editorsChoice'] = $this->editorsChoice;
503
        $array['installs'] = $this->installs;
504
        $array['numberVoters'] = $this->numberVoters;
505
        $array['histogramRating'] = $this->histogramRating;
506
        $array['price'] = $this->price;
507
        $array['currency'] = $this->currency;
508
        $array['offersIAP'] = $this->isContainsIAP();
509
        $array['offersIAPCost'] = $this->offersIAPCost;
510
        $array['containsAds'] = $this->containsAds;
511
        $array['appVersion'] = $this->appVersion;
512
        $array['androidVersion'] = $this->androidVersion;
513
        $array['minAndroidVersion'] = $this->minAndroidVersion;
514
        $array['contentRating'] = $this->contentRating;
515
        $array['released'] = $this->released !== null ? $this->released->format(\DateTime::RFC3339) : null;
516
        $array['releasedTimestamp'] = $this->released !== null ? $this->released->getTimestamp() : 0;
517
        $array['updated'] = $this->updated !== null ? $this->updated->format(\DateTime::RFC3339) : null;
518
        $array['updatedTimestamp'] = $this->updated !== null ? $this->updated->getTimestamp() : 0;
519
        $array['numberReviews'] = $this->numberReviews;
520
        $array['reviews'] = array_map(
521
            static function (Review $review) {
522
                return $review->asArray();
523
            },
524
            $this->reviews
525
        );
526
527
        return $array;
528
    }
529
}
530