Passed
Branch develop (2fd4b5)
by Alexey
01:46
created

GoogleImage::saveAs()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6.0106

Importance

Changes 0
Metric Value
cc 6
eloc 15
nc 6
nop 1
dl 0
loc 23
ccs 14
cts 15
cp 0.9333
crap 6.0106
rs 9.2222
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 GuzzleHttp\Exception\GuzzleException;
13
use GuzzleHttp\RequestOptions;
14
use Nelexa\GPlay\Exception\GooglePlayException;
15
use Nelexa\GPlay\Http\HttpClient;
16
17
/**
18
 * Contains a link to the image, allows you to customize its size and download it.
19
 *
20
 * This class only works with images stored on googleusercontent.com.
21
 * To modify the image, special parameters are added to the URL, using a hyphen.
22
 *
23
 * **Supported parameters:**
24
 *
25
 * | Param | Name         | Description                                     | Example                       |
26
 * | :---: |:------------ | :---------------------------------------------- | ----------------------------: |
27
 * | sN | size            | Sets the longer of height or width to N pixels  | s70 ![][_s] ![][_s2] ![][_s3] |
28
 * | wN | width           | Sets width of image to N pixels                 | w70 ![][_w] ![][_w2] ![][_w3] |
29
 * | hN | height          | Sets height of image to N pixels                | h70 ![][_h] ![][_h2] ![][_h3] |
30
 * | c  | square crop     | Sets square crop                   | w40-h70-c ![][_c1.1] ![][_c1.2] ![][_c1.3] |
31
 * |    |                 |                                    | w70-h40-c ![][_c2.1] ![][_c2.2] ![][_c2.3] |
32
 * |    |                 |                                    | w70-h70-c ![][_c3.1] ![][_c3.2] ![][_c3.3] |
33
 * | p  | smart crop      | Sets smart crop                    | w40-h70-p ![][_p1.1] ![][_p1.2] ![][_p1.3] |
34
 * |    |                 |                                    | w70-h40-p ![][_p2.1] ![][_p2.2] ![][_p2.3] |
35
 * |    |                 |                                    | w70-h70-p ![][_p3.1] ![][_p3.2] ![][_p3.3] |
36
 * | bN | border          | Sets border of image to N pixels            | s70-b10 ![][_b] ![][_b2] ![][_b3] |
37
 * | fv | vertical flip   | Vertically flips the image                | s70-fv ![][_fv] ![][_fv2] ![][_fv3] |
38
 * | fh | horizontal flip | Horizontally flips the image              | s70-fh ![][_fh] ![][_fh2] ![][_fh3] |
39
 *
40
 * [_s]:https://lh3.googleusercontent.com/6EtT4dght1QF9-XYvSiwx2uqkBiOnrwq-N-dPZLUw4x61Bh2Bp_w6BZ_d0dZPoTBVqM=s70
41
 * [_w]:https://lh3.googleusercontent.com/6EtT4dght1QF9-XYvSiwx2uqkBiOnrwq-N-dPZLUw4x61Bh2Bp_w6BZ_d0dZPoTBVqM=w70
42
 * [_h]:https://lh3.googleusercontent.com/6EtT4dght1QF9-XYvSiwx2uqkBiOnrwq-N-dPZLUw4x61Bh2Bp_w6BZ_d0dZPoTBVqM=h70
43
 * [_c1.1]:https://lh3.googleusercontent.com/6EtT4dght1QF9-XYvSiwx2uqkBiOnrwq-N-dPZLUw4x61Bh2Bp_w6BZ_d0dZPoTBVqM=w40-h70-c
44
 * [_c2.1]:https://lh3.googleusercontent.com/6EtT4dght1QF9-XYvSiwx2uqkBiOnrwq-N-dPZLUw4x61Bh2Bp_w6BZ_d0dZPoTBVqM=w70-h40-c
45
 * [_c3.1]:https://lh3.googleusercontent.com/6EtT4dght1QF9-XYvSiwx2uqkBiOnrwq-N-dPZLUw4x61Bh2Bp_w6BZ_d0dZPoTBVqM=w70-h70-c
46
 * [_p1.1]:https://lh3.googleusercontent.com/6EtT4dght1QF9-XYvSiwx2uqkBiOnrwq-N-dPZLUw4x61Bh2Bp_w6BZ_d0dZPoTBVqM=w40-h70-p
47
 * [_p2.1]:https://lh3.googleusercontent.com/6EtT4dght1QF9-XYvSiwx2uqkBiOnrwq-N-dPZLUw4x61Bh2Bp_w6BZ_d0dZPoTBVqM=w70-h40-p
48
 * [_p3.1]:https://lh3.googleusercontent.com/6EtT4dght1QF9-XYvSiwx2uqkBiOnrwq-N-dPZLUw4x61Bh2Bp_w6BZ_d0dZPoTBVqM=w70-h70-p
49
 * [_b]:https://lh3.googleusercontent.com/6EtT4dght1QF9-XYvSiwx2uqkBiOnrwq-N-dPZLUw4x61Bh2Bp_w6BZ_d0dZPoTBVqM=s70-b10
50
 * [_fv]:https://lh3.googleusercontent.com/6EtT4dght1QF9-XYvSiwx2uqkBiOnrwq-N-dPZLUw4x61Bh2Bp_w6BZ_d0dZPoTBVqM=s70-fv
51
 * [_fh]:https://lh3.googleusercontent.com/6EtT4dght1QF9-XYvSiwx2uqkBiOnrwq-N-dPZLUw4x61Bh2Bp_w6BZ_d0dZPoTBVqM=s70-fh
52
 *
53
 * [_s2]:https://lh3.googleusercontent.com/7tB9mdZ61rXn1uhgPVeGDV39FMtce_bDxyFcRMKlbZy_AbGP6rHn8BknJI4n-U4hki8p=s70
54
 * [_w2]:https://lh3.googleusercontent.com/7tB9mdZ61rXn1uhgPVeGDV39FMtce_bDxyFcRMKlbZy_AbGP6rHn8BknJI4n-U4hki8p=w70
55
 * [_h2]:https://lh3.googleusercontent.com/7tB9mdZ61rXn1uhgPVeGDV39FMtce_bDxyFcRMKlbZy_AbGP6rHn8BknJI4n-U4hki8p=h70
56
 * [_c1.2]:https://lh3.googleusercontent.com/7tB9mdZ61rXn1uhgPVeGDV39FMtce_bDxyFcRMKlbZy_AbGP6rHn8BknJI4n-U4hki8p=w40-h70-c
57
 * [_c2.2]:https://lh3.googleusercontent.com/7tB9mdZ61rXn1uhgPVeGDV39FMtce_bDxyFcRMKlbZy_AbGP6rHn8BknJI4n-U4hki8p=w70-h40-c
58
 * [_c3.2]:https://lh3.googleusercontent.com/7tB9mdZ61rXn1uhgPVeGDV39FMtce_bDxyFcRMKlbZy_AbGP6rHn8BknJI4n-U4hki8p=w70-h70-c
59
 * [_p1.2]:https://lh3.googleusercontent.com/7tB9mdZ61rXn1uhgPVeGDV39FMtce_bDxyFcRMKlbZy_AbGP6rHn8BknJI4n-U4hki8p=w40-h70-p
60
 * [_p2.2]:https://lh3.googleusercontent.com/7tB9mdZ61rXn1uhgPVeGDV39FMtce_bDxyFcRMKlbZy_AbGP6rHn8BknJI4n-U4hki8p=w70-h40-p
61
 * [_p3.2]:https://lh3.googleusercontent.com/7tB9mdZ61rXn1uhgPVeGDV39FMtce_bDxyFcRMKlbZy_AbGP6rHn8BknJI4n-U4hki8p=w70-h70-p
62
 * [_b2]:https://lh3.googleusercontent.com/7tB9mdZ61rXn1uhgPVeGDV39FMtce_bDxyFcRMKlbZy_AbGP6rHn8BknJI4n-U4hki8p=s70-b10
63
 * [_fv2]:https://lh3.googleusercontent.com/7tB9mdZ61rXn1uhgPVeGDV39FMtce_bDxyFcRMKlbZy_AbGP6rHn8BknJI4n-U4hki8p=s70-fv
64
 * [_fh2]:https://lh3.googleusercontent.com/7tB9mdZ61rXn1uhgPVeGDV39FMtce_bDxyFcRMKlbZy_AbGP6rHn8BknJI4n-U4hki8p=s70-fh
65
 *
66
 * [_s3]:https://lh3.googleusercontent.com/tCijG_gfFddONMX6aDD8RjnohoVy0TNbx5wc_Jn9ERSBBXIVtMqO_vs1h-v_FPFrzA0=s70
67
 * [_w3]:https://lh3.googleusercontent.com/tCijG_gfFddONMX6aDD8RjnohoVy0TNbx5wc_Jn9ERSBBXIVtMqO_vs1h-v_FPFrzA0=w70
68
 * [_h3]:https://lh3.googleusercontent.com/tCijG_gfFddONMX6aDD8RjnohoVy0TNbx5wc_Jn9ERSBBXIVtMqO_vs1h-v_FPFrzA0=h70
69
 * [_c1.3]:https://lh3.googleusercontent.com/tCijG_gfFddONMX6aDD8RjnohoVy0TNbx5wc_Jn9ERSBBXIVtMqO_vs1h-v_FPFrzA0=w40-h70-c
70
 * [_c2.3]:https://lh3.googleusercontent.com/tCijG_gfFddONMX6aDD8RjnohoVy0TNbx5wc_Jn9ERSBBXIVtMqO_vs1h-v_FPFrzA0=w70-h40-c
71
 * [_c3.3]:https://lh3.googleusercontent.com/tCijG_gfFddONMX6aDD8RjnohoVy0TNbx5wc_Jn9ERSBBXIVtMqO_vs1h-v_FPFrzA0=w70-h70-c
72
 * [_p1.3]:https://lh3.googleusercontent.com/tCijG_gfFddONMX6aDD8RjnohoVy0TNbx5wc_Jn9ERSBBXIVtMqO_vs1h-v_FPFrzA0=w40-h70-p
73
 * [_p2.3]:https://lh3.googleusercontent.com/tCijG_gfFddONMX6aDD8RjnohoVy0TNbx5wc_Jn9ERSBBXIVtMqO_vs1h-v_FPFrzA0=w70-h40-p
74
 * [_p3.3]:https://lh3.googleusercontent.com/tCijG_gfFddONMX6aDD8RjnohoVy0TNbx5wc_Jn9ERSBBXIVtMqO_vs1h-v_FPFrzA0=w70-h70-p
75
 * [_b3]:https://lh3.googleusercontent.com/tCijG_gfFddONMX6aDD8RjnohoVy0TNbx5wc_Jn9ERSBBXIVtMqO_vs1h-v_FPFrzA0=s70-b10
76
 * [_fv3]:https://lh3.googleusercontent.com/tCijG_gfFddONMX6aDD8RjnohoVy0TNbx5wc_Jn9ERSBBXIVtMqO_vs1h-v_FPFrzA0=s70-fv
77
 * [_fh3]:https://lh3.googleusercontent.com/tCijG_gfFddONMX6aDD8RjnohoVy0TNbx5wc_Jn9ERSBBXIVtMqO_vs1h-v_FPFrzA0=s70-fh
78
 *
79
 * If the URL has no parameters, by default GoogleUserContents uses the parameter **s512**.
80
 * This means that the width or height will not exceed 512px.
81
 *
82
 * @see https://developers.google.com/people/image-sizing Goolge People API - Image Sizing.
83
 * @see https://github.com/null-dev/libGoogleUserContent Java library to create googleusercontent.com URLs.
84
 * @see https://sites.google.com/site/picasaresources/Home/Picasa-FAQ/google-photos-1/how-to/how-to-get-a-direct-link-to-an-image
85
 *     Google Photos and Picasa: How to get a direct link to an image (of a specific size)
86
 */
87
class GoogleImage
88
{
89
    private const
90
        PARAM_SIZE = 's',
91
        PARAM_WIDTH = 'w',
92
        PARAM_HEIGHT = 'h',
93
        PARAM_BORDER = 'b',
94
        PARAM_SQUARE_CROP = 'c',
95
        PARAM_SMART_CROP = 'p',
96
        PARAM_FLIP_VERTICAL = 'fv',
97
        PARAM_FLIP_HORIZONTAL = 'fh';
98
99
    /** @var string Base URL without parameters and file name. */
100
    private $baseUrl;
101
102
    /** @var int|null Size longer of height or width to N pixels or `null`. */
103
    private $size;
104
105
    /** @var int|null Image width size up to N pixels. */
106
    private $width;
107
108
    /** @var int|null Image height size up to N pixels. */
109
    private $height;
110
111
    /** @var int|null Image border size or `null`. */
112
    private $border;
113
114
    /**
115
     * Using square crop.
116
     *
117
     * When cropping, only the center of the image is saved.
118
     *
119
     * @var bool Using square crop.
120
     */
121
    private $squareCrop = false;
122
123
    /**
124
     * Using smart crop.
125
     *
126
     * When cropping, some algorithm searches for the most interesting part of the image,
127
     * so the result of cropping does not always preserve the center of the image.
128
     *
129
     * @var bool Using smart crop.
130
     */
131
    private $smartCrop = false;
132
133
    /** @var bool Vertical flip effect */
134
    private $verticalFlip = false;
135
136
    /** @var bool Horizontal flip effect */
137
    private $horizontalFlip = false;
138
139
    /**
140
     * Variant URL with file name at the end.
141
     *
142
     * A special URL structure is used. URL starts with /-.
143
     *
144
     * Example URL:
145
     * https://lh3.googleusercontent.com/-LB59qNIqtS4/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rf3YR_W16kFTuh5tCgHpZ02_ndQOg/s100-no/photo.jpg
146
     *
147
     * @var bool Special URL structure is used.
148
     */
149
    private $variantOfUrlWithFileName = false;
150
151
    /**
152
     * Creates a GoogleImage object from the URL of the googleusercontent.com.
153
     *
154
     * @param string $url URL image of googleusercontent.com server
155
     * @param bool $keepParams Keep parameters from URL.
156
     *
157
     * @throws \InvalidArgumentException If the URL is not an image from the
158
     *     hosts *.googleusercontent.com, *.ggpht.com or *.bp.blogspot.com.
159
     */
160 54
    public function __construct(string $url, bool $keepParams = true)
161
    {
162 54
        $httpComponents = parse_url($url);
163
        if (
164 54
            !isset($httpComponents['host']) ||
165 54
            !preg_match('~\.(googleusercontent\.com|ggpht\.com|bp\.blogspot\.com)$~i', $httpComponents['host'])
166
        ) {
167 5
            throw new \InvalidArgumentException(sprintf('Unsupported URL: %s', $url));
168
        }
169 49
        $path = ltrim($httpComponents['path'], '/');
170 49
        $parts = explode('/', $path);
171 49
        $paramString = null;
172 49
        if (count($parts) > 4 && strpos($parts[0], '-') === 0) {
173 15
            if (isset($parts[5]) || (isset($parts[4]) && strrpos($url, '/') === strlen($url) - 1)) {
174 14
                $paramString = $parts[4];
175
            }
176 15
            $parts = array_slice($parts, 0, 4);
177 15
            $path = implode('/', $parts);
178 15
            $url = $httpComponents['scheme'] . '://' . $httpComponents['host'] . '/' . $path . '/';
179 15
            $this->variantOfUrlWithFileName = true;
180 42
        } elseif (($pos = strpos($url, '=')) !== false) {
181 23
            $paramString = substr($url, $pos + 1);
182 23
            $url = substr($url, 0, $pos);
183
        }
184
185 49
        $this->baseUrl = $url;
186 49
        if ($keepParams && $paramString !== null) {
187 27
            $this->parseParams($paramString);
188
        }
189 49
    }
190
191
    /**
192
     * @param string $paramString Image parameters
193
     */
194 27
    private function parseParams(string $paramString): void
195
    {
196 27
        $params = explode('-', $paramString);
197 27
        foreach ($params as $param) {
198 27
            if (empty($param)) {
199 1
                continue;
200
            }
201
202 26
            $command = $param[0]; // 1 char
203
            switch ($command) {
204 26
                case self::PARAM_SIZE:
205 7
                    $arg = (int)substr($param, 1);
206 7
                    $this->setSize($arg);
207 7
                    break;
208 25
                case self::PARAM_WIDTH:
209 11
                    $arg = (int)substr($param, 1);
210 11
                    $this->setWidth($arg);
211 11
                    break;
212 21
                case self::PARAM_HEIGHT:
213 6
                    $arg = (int)substr($param, 1);
214 6
                    $this->setHeight($arg);
215 6
                    break;
216 17
                case self::PARAM_BORDER:
217 2
                    $arg = (int)substr($param, 1);
218 2
                    $this->setBorder($arg);
219 2
                    break;
220 17
                case self::PARAM_SQUARE_CROP:
221 1
                    $this->setSquareCrop(true);
222 1
                    break;
223 16
                case self::PARAM_SMART_CROP:
224
                    $this->setSmartCrop(true);
225
                    break;
226
                default:
227
                    switch ($param) {
228 16
                        case self::PARAM_FLIP_VERTICAL:
229 1
                            $this->setVerticalFlip(true);
230 1
                            break;
231 15
                        case self::PARAM_FLIP_HORIZONTAL:
232 1
                            $this->setHorizontalFlip(true);
233 1
                            break;
234
                        // ignore other parameters
235
                    }
236
            }
237
        }
238 27
    }
239
240
    /**
241
     * Returns the URL of the image with all the parameters set.
242
     *
243
     * @return string Image URL.
244
     */
245 35
    public function getUrl(): string
246
    {
247 35
        $params = [];
248 35
        if ($this->size !== null) {
249 14
            $params[] = self::PARAM_SIZE . $this->size;
250
        } else {
251 23
            if ($this->width !== null) {
252 14
                $params[] = self::PARAM_WIDTH . $this->width;
253
            }
254 23
            if ($this->height !== null) {
255 10
                $params[] = self::PARAM_HEIGHT . $this->height;
256
            }
257
        }
258
259 35
        if ($this->isValidSmartCrop()) {
260 4
            $params[] = self::PARAM_SMART_CROP;
261 33
        } elseif ($this->squareCrop) {
262 7
            $params[] = self::PARAM_SQUARE_CROP;
263
        }
264
265 35
        if ($this->variantOfUrlWithFileName) {
266 7
            if (empty($params)) {
267 3
                return $this->baseUrl;
268
            }
269 5
            return $this->baseUrl . implode('-', $params) . '/';
270
        }
271
272 28
        if ($this->border !== null) {
273 3
            $params[] = self::PARAM_BORDER . $this->border;
274
        }
275
276 28
        if ($this->verticalFlip) {
277 2
            $params[] = self::PARAM_FLIP_VERTICAL;
278
        }
279
280 28
        if ($this->horizontalFlip) {
281 2
            $params[] = self::PARAM_FLIP_HORIZONTAL;
282
        }
283
284 28
        if (empty($params)) {
285 6
            return $this->baseUrl;
286
        }
287 23
        return $this->baseUrl . '=' . implode('-', $params);
288
    }
289
290
    /**
291
     * @return bool
292
     */
293 35
    private function isValidSmartCrop(): bool
294
    {
295 35
        return $this->smartCrop && (
296 4
            $this->size !== null ||
297 3
                ($this->width !== null && $this->height !== null) ||
298 35
                ($this->size === null && $this->width === null && $this->height === null)
299
            );
300
    }
301
302
    /**
303
     * Returns a URL with the original image size.
304
     *
305
     * @return string URL of the original image size.
306
     */
307 19
    public function getOriginalSizeUrl(): string
308
    {
309 19
        $params = [self::PARAM_SIZE . '0'];
310 19
        if ($this->variantOfUrlWithFileName) {
311 6
            return $this->baseUrl . implode('-', $params) . '/';
312
        }
313 13
        return $this->baseUrl . '=' . implode('-', $params);
314
    }
315
316
    /**
317
     * Returns a hash value for this object.
318
     * Can be used to generate a file save path.
319
     *
320
     * @param string $hashAlgorithm Hash algorithm. Default is md5.
321
     * @param int $parts Nesting path. Maximum 6.
322
     * @param int $partLength The length of the nested path.
323
     *
324
     * @return string Unique hash value of this object.
325
     */
326 3
    public function getHashUrl(string $hashAlgorithm = 'md5', int $parts = 0, int $partLength = 2): string
327
    {
328 3
        $hash = hash($hashAlgorithm, $this->getUrl());
329 3
        $hashLength = strlen($hash);
330 3
        $parts = max(0, min(6, $parts));
331 3
        if ($parts > 0) {
332 2
            $partLength = max(1, min($partLength, (int)($hashLength / $parts)));
333 2
            $partsBuild = [];
334 2
            for ($i = 0; $i < $parts; $i++) {
335 2
                $partsBuild[] = substr($hash, $i * $partLength, $partLength);
336
            }
337 2
            $hash = implode('/', $partsBuild) . '/' . $hash;
338
        }
339 3
        return $hash;
340
    }
341
342
    /**
343
     * Sets the original image size.
344
     *
345
     * @return GoogleImage Returns the same object \Nelexa\GPlay\Model\GoogleImage to support the call chain.
346
     */
347 1
    public function useOriginalSize(): GoogleImage
348
    {
349 1
        $this->setSize(0);
350 1
        return $this;
351
    }
352
353
    /**
354
     * Sets the image size greater than height or width up to N pixels.
355
     *
356
     * @param int|null $size Width or height of the image in pixels.
357
     *
358
     * @return GoogleImage Returns the same object \Nelexa\GPlay\Model\GoogleImage to support the call chain.
359
     */
360 22
    public function setSize(?int $size): GoogleImage
361
    {
362 22
        $this->size = $size;
363 22
        $this->width = null;
364 22
        $this->height = null;
365 22
        return $this;
366
    }
367
368
    /**
369
     * Sets the width of the image.
370
     *
371
     * @param int|null $width Image width.
372
     *
373
     * @return GoogleImage Returns the same object \Nelexa\GPlay\Model\GoogleImage to support the call chain.
374
     */
375 16
    public function setWidth(?int $width): GoogleImage
376
    {
377 16
        $this->width = $width;
378 16
        $this->size = null;
379 16
        return $this;
380
    }
381
382
    /**
383
     * Sets the height of the image.
384
     *
385
     * @param int|null $height Image height.
386
     *
387
     * @return GoogleImage Returns the same object \Nelexa\GPlay\Model\GoogleImage to support the call chain.
388
     */
389 11
    public function setHeight(?int $height): GoogleImage
390
    {
391 11
        $this->height = $height;
392 11
        $this->size = null;
393 11
        return $this;
394
    }
395
396
    /**
397
     * Sets the width and height of the image.
398
     *
399
     * @param int $width Image width.
400
     * @param int $height Image height.
401
     *
402
     * @return GoogleImage Returns the same object \Nelexa\GPlay\Model\GoogleImage to support the call chain.
403
     */
404
    public function setWidthAndHeight(int $width, int $height): GoogleImage
405
    {
406
        $this->width = $width;
407
        $this->height = $height;
408
        $this->size = null;
409
        return $this;
410
    }
411
412
    /**
413
     * Sets the border around the image.
414
     *
415
     * @param int|null $border The number of pixels of the border.
416
     *
417
     * @return GoogleImage Returns the same object \Nelexa\GPlay\Model\GoogleImage to support the call chain.
418
     */
419 4
    public function setBorder(?int $border): GoogleImage
420
    {
421 4
        $this->border = $border;
422 4
        return $this;
423
    }
424
425
    /**
426
     * Sets the use of square crop.
427
     *
428
     * @param bool $squareCrop Square crop.
429
     *
430
     * @return GoogleImage Returns the same object \Nelexa\GPlay\Model\GoogleImage to support the call chain.
431
     */
432 7
    public function setSquareCrop(bool $squareCrop): GoogleImage
433
    {
434 7
        $this->squareCrop = $squareCrop;
435 7
        $this->smartCrop = false;
436 7
        return $this;
437
    }
438
439
    /**
440
     * Sets the use of smart crop.
441
     *
442
     * @param bool $smartCrop Smart crop.
443
     *
444
     * @return GoogleImage Returns the same object \Nelexa\GPlay\Model\GoogleImage to support the call chain.
445
     */
446 4
    public function setSmartCrop(bool $smartCrop): GoogleImage
447
    {
448 4
        $this->smartCrop = $smartCrop;
449 4
        $this->squareCrop = false;
450 4
        return $this;
451
    }
452
453
    /**
454
     * Sets the use of vertical flip.
455
     *
456
     * @param bool $verticalFlip Vertical flip.
457
     *
458
     * @return GoogleImage Returns the same object \Nelexa\GPlay\Model\GoogleImage to support the call chain.
459
     */
460 3
    public function setVerticalFlip(bool $verticalFlip): GoogleImage
461
    {
462 3
        $this->verticalFlip = $verticalFlip;
463 3
        return $this;
464
    }
465
466
    /**
467
     * Sets the use of horizontal flip.
468
     *
469
     * @param bool $horizontalFlip Horizontal flip.
470
     * @return GoogleImage Returns the same object \Nelexa\GPlay\Model\GoogleImage to support the call chain.
471
     */
472 3
    public function setHorizontalFlip(bool $horizontalFlip): GoogleImage
473
    {
474 3
        $this->horizontalFlip = $horizontalFlip;
475 3
        return $this;
476
    }
477
478
    /**
479
     * @return int|null
480
     * @ignore
481
     */
482 1
    public function getSize(): ?int
483
    {
484 1
        return $this->size;
485
    }
486
487
    /**
488
     * @return int|null
489
     * @ignore
490
     */
491 1
    public function getWidth(): ?int
492
    {
493 1
        return $this->width;
494
    }
495
496
    /**
497
     * @return int|null
498
     * @ignore
499
     */
500 1
    public function getHeight(): ?int
501
    {
502 1
        return $this->height;
503
    }
504
505
    /**
506
     * @return int|null
507
     * @ignore
508
     */
509 1
    public function getBorder(): ?int
510
    {
511 1
        return $this->border;
512
    }
513
514
    /**
515
     * @return bool
516
     * @ignore
517
     */
518 1
    public function isSquareCrop(): bool
519
    {
520 1
        return $this->squareCrop;
521
    }
522
523
    /**
524
     * @return bool
525
     * @ignore
526
     */
527 1
    public function isSmartCrop(): bool
528
    {
529 1
        return $this->smartCrop;
530
    }
531
532
    /**
533
     * @return bool
534
     * @ignore
535
     */
536 1
    public function isVerticalFlip(): bool
537
    {
538 1
        return $this->verticalFlip;
539
    }
540
541
    /**
542
     * @return bool
543
     * @ignore
544
     */
545 1
    public function isHorizontalFlip(): bool
546
    {
547 1
        return $this->horizontalFlip;
548
    }
549
550
    /**
551
     * Reset all parameters.
552
     */
553 3
    public function reset(): void
554
    {
555 3
        $this->size = null;
556 3
        $this->width = null;
557 3
        $this->height = null;
558 3
        $this->border = null;
559 3
        $this->squareCrop = false;
560 3
        $this->smartCrop = false;
561 3
        $this->verticalFlip = false;
562 3
        $this->horizontalFlip = false;
563 3
    }
564
565
    /**
566
     * Save image to disk.
567
     *
568
     * @param string $destPath Output filename.
569
     *
570
     * @return ImageInfo Returns the object {@see \Nelexa\GPlay\Model\ImageInfo}.
571
     *
572
     * @throws GooglePlayException If the error is HTTP or save to disk.
573
     */
574 2
    public function saveAs(string $destPath): ImageInfo
575
    {
576 2
        $url = $this->getUrl();
577
578 2
        $dirName = dirname($destPath);
579 2
        if (!is_dir($dirName) && !mkdir($dirName, 0755, true) && !is_dir($dirName)) {
580
            throw new \RuntimeException(sprintf('Failed to create "%s"', $dirName));
581
        }
582
583
        try {
584 2
            $this->getHttpClient()->request('GET', $url, [
585 2
                RequestOptions::SINK => $destPath,
586 2
                RequestOptions::HTTP_ERRORS => true,
587
            ]);
588
589 1
            return new ImageInfo($url, $destPath);
590 1
        } catch (\Throwable|GuzzleException $e) {
591 1
            if (is_file($destPath)) {
592 1
                unlink($destPath);
593
            }
594 1
            $ge = new GooglePlayException($e->getMessage(), $e->getCode(), $e);
595 1
            $ge->setUrl($url);
596 1
            throw $ge;
597
        }
598
    }
599
600
    /**
601
     * @return HttpClient
602
     */
603 2
    private function getHttpClient(): HttpClient
604
    {
605 2
        static $httpClient;
606
607 2
        if ($httpClient === null) {
608 1
            $httpClient = new HttpClient();
609
        }
610 2
        return $httpClient;
611
    }
612
613
    /**
614
     * Returns binary image contents.
615
     *
616
     * @return string Binary image content.
617
     *
618
     * @throws GooglePlayException If an HTTP error occurred.
619
     */
620
    public function getBinaryImageContent(): string
621
    {
622
        $url = $this->getUrl();
623
624
        try {
625
            $response = $this->getHttpClient()->request('GET', $url, [
626
                RequestOptions::HTTP_ERRORS => true,
627
            ]);
628
            return $response->getBody()->getContents();
629
        } catch (\Throwable|GuzzleException $e) {
630
            $ge = new GooglePlayException($e->getMessage(), $e->getCode(), $e);
631
            $ge->setUrl($url);
632
            throw $ge;
633
        }
634
    }
635
636
    /**
637
     * @return array
638
     * @ignore
639
     */
640
    public function __debugInfo()
641
    {
642
        return [
643
            'url' => $this->getUrl(),
644
        ];
645
    }
646
647
    /**
648
     * Returns the URL of the image.
649
     *
650
     * This method is equivalent to {@see GoogleImage::getUrl()}.
651
     *
652
     * @return string Image URL.
653
     */
654 1
    public function __toString()
655
    {
656 1
        return $this->getUrl();
657
    }
658
}
659