Passed
Branch develop (29ed49)
by Alexey
01:58
created

AppDetail::validateBuilder()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

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