Wallpaper::getViews()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 3
eloc 4
nc 2
nop 0
1
<?php
2
3
namespace Wallhaven;
4
5
use DateTime;
6
use GuzzleHttp\Client;
7
use GuzzleHttp\Exception\RequestException;
8
use PHPHtmlParser\Dom;
9
use Wallhaven\Exceptions\DownloadException;
10
use Wallhaven\Exceptions\LoginException;
11
use Wallhaven\Exceptions\NotFoundException;
12
use Wallhaven\Exceptions\ParseException;
13
14
/**
15
 * Wallpaper
16
 *
17
 * @package Wallhaven
18
 */
19
class Wallpaper
20
{
21
    /**
22
     * @var Client
23
     */
24
    private $client;
25
26
    /**
27
     * @var bool Cache enabled.
28
     */
29
    private $cacheEnabled;
30
31
    /**
32
     * @var Dom Cached DOM.
33
     */
34
    private $dom;
35
36
    /**
37
     * @var int Wallpaper ID.
38
     */
39
    private $id;
40
41
    private $tags;
42
    private $purity;
43
    private $resolution;
44
    private $size;
45
    private $category;
46
    private $views;
47
    private $favorites;
48
    private $featuredBy;
49
    private $featuredDate;
50
    private $uploadedBy;
51
    private $uploadedDate;
52
53
    private $imageUrl;
54
55
56
    /**
57
     * @param int    $id     Wallpaper's ID.
58
     * @param Client $client HTTP Client.
59
     */
60
    public function __construct($id, Client $client)
61
    {
62
        $this->id = $id;
63
        $this->client = $client;
64
        $this->cacheEnabled = true;
65
    }
66
67
    /**
68
     * @return int Wallpaper ID.
69
     */
70
    public function getId()
71
    {
72
        return $this->id;
73
    }
74
75
    /**
76
     * @param array $properties Properties.
77
     */
78
    public function setProperties(array $properties)
79
    {
80
        foreach ($properties as $key => $value) {
81
            $this->$key = $value;
82
        }
83
    }
84
85
    /**
86
     * Enable or disable caching of wallpaper information. It's recommended to leave this enabled (default) unless
87
     * you really need real-time information. If you disable caching, performance will be severely degraded.
88
     *
89
     * @param bool $enabled Whether caching should be enabled.
90
     */
91
    public function setCacheEnabled($enabled)
92
    {
93
        $this->cacheEnabled = $enabled;
94
    }
95
96
    /**
97
     * Get wallpaper tags.
98
     *
99
     * @return string[] Tags.
100
     */
101
    public function getTags()
102
    {
103
        if ($this->cacheEnabled && $this->tags !== null) {
104
            return $this->tags;
105
        }
106
107
        $dom = $this->getDom();
108
109
        $this->tags = [];
110
111
        foreach ($dom->find('a.tagname') as $e) {
0 ignored issues
show
Bug introduced by
The expression $dom->find('a.tagname') of type array|object<PHPHtmlParser\Dom\AbstractNode> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
112
            $this->tags[] = $e->text;
113
        }
114
115
        return $this->tags;
116
    }
117
118
    /**
119
     * @return Dom
120
     * @throws LoginException  Thrown if access to the wallpaper was denied.
121
     * @throws NotFoundException Thrown if the wallpaper was not found.
122
     */
123
    private function getDom()
124
    {
125
        if ($this->cacheEnabled && $this->dom !== null) {
126
            return $this->dom;
127
        }
128
129
        try {
130
            $response = $this->client->get(Wallhaven::URL_WALLPAPER . '/' . $this->id)->getBody()->getContents();
131
        } catch (RequestException $e) {
132
            $code = $e->getCode();
133
            if ($code == 403) {
134
                throw new LoginException("Access to wallpaper is forbidden.");
135
            } else if ($code == 404) {
136
                throw new NotFoundException("Wallpaper not found.");
137
            } else {
138
                throw $e;
139
            }
140
        }
141
142
        $dom = new Dom();
143
        $dom->load($response);
144
145
        $this->dom = $this->cacheEnabled ? $dom : null;
146
147
        return $dom;
148
    }
149
150
    /**
151
     * @return int Purity.
152
     */
153
    public function getPurity()
154
    {
155
        if ($this->cacheEnabled && $this->purity !== null) {
156
            return $this->purity;
157
        }
158
159
        $dom = $this->getDom();
160
161
        $purityClass
162
            = $dom->find('#wallpaper-purity-form')[0]->find('fieldset.framed')[0]->find('input[checked="checked"]')[0]
163
            ->nextSibling()->getAttribute('class');
164
165
        $purityText = preg_split("/purity /", $purityClass)[1];
166
167
        $this->purity = constant('Wallhaven\Purity::' . strtoupper($purityText));
168
169
        return $this->purity;
170
    }
171
172
    /**
173
     * @return string Resolution.
174
     */
175
    public function getResolution()
176
    {
177
        if (!$this->cacheEnabled || $this->resolution === null) {
178
            $resolutionElement = $this->getDom()->find('h3.showcase-resolution');
179
            $this->resolution = str_replace(' ', '', $resolutionElement->text);
180
        }
181
182
        return $this->resolution;
183
    }
184
185
    /**
186
     * @param string $contents
187
     *
188
     * @return \PHPHtmlParser\Dom\AbstractNode
189
     * @throws ParseException
190
     */
191
    private function getSibling($contents)
192
    {
193
        $dom = $this->getDom();
194
195
        $result = $dom->find('div[data-storage-id="showcase-info"]')[0]->find('dl')[0]->find('dt');
196
197
        foreach ($result as $e) {
198
            if ($e->text == $contents) {
199
                return $e->nextSibling();
200
            }
201
        }
202
203
        throw new ParseException("Sibling of element with content \"" . $contents . "\" not found.");
204
    }
205
206
    /**
207
     * @return string Size of the image.
208
     */
209
    public function getSize()
210
    {
211
        if (!$this->cacheEnabled || $this->size === null) {
212
            $this->size = $this->getSibling("Size")->text;
213
        }
214
215
        return $this->size;
216
    }
217
218
    /**
219
     * @return int Category.
220
     */
221
    public function getCategory()
222
    {
223
        if (!$this->cacheEnabled || $this->category === null) {
224
            $this->category = constant('Wallhaven\Category::' . strtoupper($this->getSibling("Category")->text));
225
        }
226
227
        return $this->category;
228
    }
229
230
    /**
231
     * @return int Number of views.
232
     */
233
    public function getViews()
234
    {
235
        if (!$this->cacheEnabled || $this->views === null) {
236
            $this->views = (int)str_replace(',', '', $this->getSibling("Views")->text);
237
        }
238
239
        return $this->views;
240
    }
241
242
    /**
243
     * @return int Number of favorites.
244
     */
245
    public function getFavorites()
246
    {
247
        if (!$this->cacheEnabled || $this->favorites === null) {
248
            $favsLink = $this->getSibling("Favorites")->find('a');
249
250
            if (!$favsLink[0]) {
251
                $this->favorites = 0;
252
            } else {
253
                $this->favorites = (int)$favsLink[0]->text;
254
            }
255
        }
256
257
        return $this->favorites;
258
    }
259
260
    /**
261
     * @return User User that featured the wallpaper.
262
     * @throws ParseException
263
     */
264 View Code Duplication
    public function getFeaturedBy()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
265
    {
266
        if (!$this->cacheEnabled || $this->featuredBy === null) {
267
            $usernameElement = $this->getDom()
268
                ->find("footer.sidebar-section")
269
                ->find(".username");
270
271
            if ($usernameElement != null) {
272
                $this->featuredBy = new User($usernameElement->text);
273
            }
274
        }
275
276
        return $this->featuredBy;
277
    }
278
279
    /**
280
     * @return DateTime Date and time when the wallpaper was featured.
281
     * @throws ParseException
282
     */
283 View Code Duplication
    public function getFeaturedDate()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
284
    {
285
        if (!$this->cacheEnabled || $this->featuredDate === null) {
286
            $featuredDateElement = $this->getDom()
287
                ->find("footer.sidebar-section")
288
                ->find("time");
289
290
            if ($featuredDateElement != null) {
291
                $this->featuredDate = new DateTime($featuredDateElement->getAttribute('datetime'));
292
            }
293
        }
294
295
        return $this->featuredDate;
296
    }
297
298
    /**
299
     * @return User User that uploaded the wallpaper.
300
     */
301
    public function getUploadedBy()
302
    {
303
        if (!$this->cacheEnabled || $this->uploadedBy === null) {
304
            $username = $this->getDom()
305
                ->find(".showcase-uploader")
306
                ->find("a.username")
307
                ->text;
308
309
            $this->uploadedBy = new User($username);
310
        }
311
312
        return $this->uploadedBy;
313
    }
314
315
    /**
316
     * @return DateTime Date and time when the wallpaper was uploaded.
317
     */
318
    public function getUploadedDate()
319
    {
320
        if (!$this->cacheEnabled || $this->uploadedDate === null) {
321
            $timeElement = $this->getDom()->find(".showcase-uploader > time:nth-child(4)")[0];
322
323
            $this->uploadedDate = new DateTime($timeElement->getAttribute('datetime'));
324
        }
325
326
        return $this->uploadedDate;
327
    }
328
329
    public function __toString()
330
    {
331
        return "Wallpaper " . $this->id;
332
    }
333
334
    /**
335
     * @return string Thumbnail URL.
336
     */
337
    public function getThumbnailUrl()
338
    {
339
        return Wallhaven::URL_HOME . Wallhaven::URL_THUMB_PREFIX . $this->id . '.jpg';
340
    }
341
342
    /**
343
     * Download the wallpaper.
344
     *
345
     * @param string $directory Where to download the wallpaper.
346
     *
347
     * @throws DownloadException Thrown if the download directory cannot be created.
348
     */
349
    public function download($directory)
350
    {
351 View Code Duplication
        if (!file_exists($directory)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
352
            if (!@mkdir($directory, null, true)) {
353
                throw new DownloadException("The download directory cannot be created.");
354
            }
355
        }
356
357
        $url = $this->getImageUrl();
358
359
        $this->client->get($url, [
360
            'save_to' => $directory . '/' . basename($url),
361
        ]);
362
    }
363
364
    /**
365
     * @param bool $assumeJpg Assume the wallpaper is JPG. May speed up the method at the cost of potentially wrong URL.
366
     *
367
     * @return string URL.
368
     * @throws LoginException
369
     * @throws NotFoundException
370
     */
371
    public function getImageUrl($assumeJpg = false)
372
    {
373
        if ($assumeJpg) {
374
            return Wallhaven::URL_IMG_PREFIX . $this->id . '.jpg';
375
        }
376
377
        if (!$this->cacheEnabled || $this->imageUrl === null) {
378
            $dom = $this->getDom();
379
            $url = $dom->find('img#wallpaper')[0]->getAttribute('src');
380
            $this->imageUrl = (parse_url($url, PHP_URL_SCHEME) ?: "https:") . $url;
381
        }
382
383
        return $this->imageUrl;
384
    }
385
}
386