Passed
Push — master ( 80ad96...45165d )
by Alexey
03:00
created

ListScraper::__invoke()   C

Complexity

Conditions 11
Paths 25

Size

Total Lines 105
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 63
dl 0
loc 105
rs 6.6606
c 0
b 0
f 0
cc 11
nc 25
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
namespace Nelexa\GPlay\Scraper;
5
6
use Nelexa\GPlay\Exception\GooglePlayException;
7
use Nelexa\GPlay\GPlayApps;
8
use Nelexa\GPlay\Http\ResponseHandlerInterface;
9
use Nelexa\GPlay\Model\App;
10
use Nelexa\GPlay\Model\Developer;
11
use Nelexa\GPlay\Model\GoogleImage;
12
use Nelexa\GPlay\Util\ScraperUtil;
13
use Psr\Http\Message\RequestInterface;
14
use Psr\Http\Message\ResponseInterface;
15
use function GuzzleHttp\Psr7\parse_query;
16
17
class ListScraper implements ResponseHandlerInterface
18
{
19
    /**
20
     * @param RequestInterface $request
21
     * @param ResponseInterface $response
22
     * @return App[]
23
     * @throws GooglePlayException
24
     */
25
    public function __invoke(RequestInterface $request, ResponseInterface $response)
26
    {
27
        $doc = new \DOMDocument();
28
        $internalErrors = libxml_use_internal_errors(true);
29
        if (!$doc->loadHTML('<?xml encoding="utf-8" ?>' . $response->getBody()->getContents())) {
30
            throw new \RuntimeException('error load html');
31
        }
32
        libxml_use_internal_errors($internalErrors);
33
34
        $xpath = new \DOMXPath($doc);
35
        $cardNodes = $xpath->query("//div[@class and contains(concat(' ', normalize-space(@class), ' '), ' card ') and @data-docid]");
36
37
        $apps = [];
38
        /**
39
         * @var \DOMElement $cardNode
40
         */
41
        foreach ($cardNodes as $cardNode) {
42
            {
43
                $appId = $cardNode->getAttribute('data-docid');
44
            }
45
46
            {
47
                $nodeTitle = $xpath->query('.//a[@class="title"]', $cardNode)->item(0);
48
                if ($nodeTitle === null) {
49
                    throw (new GooglePlayException('Error parse app list'))
50
                        ->setUrl($request->getUri()->__toString());
51
                }
52
                $url = GPlayApps::GOOGLE_PLAY_URL . $nodeTitle->attributes->getNamedItem('href')->textContent;
53
                $name = trim($nodeTitle->attributes->getNamedItem('title')->textContent);
54
            }
55
56
            {
57
                $descriptionNode = $xpath->query('.//div[@class="description"]', $cardNode)->item(0);
58
                $summary = null;
59
                if ($descriptionNode !== null) {
60
                    $summary = ScraperUtil::html2text($descriptionNode->textContent);
61
                }
62
            }
63
64
            {
65
                $developerNode = $xpath->query('.//a[@class="subtitle"]', $cardNode)->item(0);
66
                if ($developerNode === null) {
67
                    throw (new GooglePlayException('Error parse app list developer node'))
68
                        ->setUrl($request->getUri()->__toString());
69
                }
70
                $developerName = trim($developerNode->textContent);
71
                $developerUrl = GPlayApps::GOOGLE_PLAY_URL . $developerNode->attributes->getNamedItem('href')->textContent;
72
                $developerId = parse_query(parse_url($developerUrl, PHP_URL_QUERY))[GPlayApps::REQ_PARAM_APP_ID];
73
            }
74
75
            {
76
                $iconNode = $xpath->query('.//img[@data-cover-large]/@src', $cardNode)->item(0);
77
                if ($iconNode === null) {
78
                    throw (new GooglePlayException('Error parse app list icon node'))
79
                        ->setUrl($request->getUri()->__toString());
80
                }
81
                $icon = new GoogleImage('https:' . $iconNode->textContent);
82
                $icon->reset();
83
            }
84
85
            {
86
                $price = null;
87
                $priceNode = $xpath->query('.//span[@class="display-price"]', $cardNode);
88
                if ($priceNode->length > 0) {
89
                    $price = trim($priceNode->item(0)->textContent);
90
                    if (empty($price)) {
91
                        $price = null;
92
                    }
93
                }
94
            }
95
96
            {
97
                $score = 0;
98
                $ratingStyleAttr = $xpath->query('.//div[@class="current-rating" and @style]/@style', $cardNode)->item(0);
99
                if ($ratingStyleAttr !== null) {
100
                    $ratingStyle = $ratingStyleAttr->textContent;
101
                    if (preg_match('/([\d\.]+)%/', $ratingStyle, $match)) {
102
                        $score = round($match[1] * 0.05, 1); // percent * 5 star and round result
103
                    }
104
                }
105
            }
106
107
            $locale = parse_query($request->getUri()->getQuery())[GPlayApps::REQ_PARAM_LOCALE] ?? GPlayApps::DEFAULT_LOCALE;
108
109
            $apps[] = new App(
110
                App::newBuilder()
111
                    ->setId($appId)
112
                    ->setUrl($url)
113
                    ->setLocale($locale)
114
                    ->setName($name)
115
                    ->setSummary($summary)
116
                    ->setDeveloper(
117
                        new Developer(
118
                            Developer::newBuilder()
119
                                ->setId($developerId)
120
                                ->setUrl($developerUrl)
121
                                ->setName($developerName)
122
                        )
123
                    )
124
                    ->setIcon($icon)
125
                    ->setScore($score)
126
                    ->setPriceText($price)
127
            );
128
        }
129
        return $apps;
130
    }
131
}
132