Issues (496)

lib/WhatsNew/WhatsNewCheck.php (5 issues)

Labels
Severity
1
<?php
2
declare(strict_types=1);
3
/**
4
 * Analytics
5
 *
6
 * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
7
 * SPDX-License-Identifier: AGPL-3.0-or-later
8
 */
9
10
namespace OCA\Analytics\WhatsNew;
11
12
use OCP\AppFramework\Db\DoesNotExistException;
0 ignored issues
show
The type OCP\AppFramework\Db\DoesNotExistException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use OCP\Http\Client\IClientService;
0 ignored issues
show
The type OCP\Http\Client\IClientService was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use OCP\Http\Client\IResponse;
0 ignored issues
show
The type OCP\Http\Client\IResponse was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use Psr\Log\LoggerInterface;
0 ignored issues
show
The type Psr\Log\LoggerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
17
class WhatsNewCheck
18
{
19
    public const RESPONSE_NO_CONTENT = 0;
20
    public const RESPONSE_USE_CACHE = 1;
21
    public const RESPONSE_HAS_CONTENT = 2;
22
    /** @var IClientService */
23
    protected $clientService;
24
    /** @var WhatsNewMapper */
25
    private $mapper;
26
    /** @var ILogger */
0 ignored issues
show
The type OCA\Analytics\WhatsNew\ILogger was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
27
    private $logger;
28
29
    public function __construct(IClientService $clientService, WhatsNewMapper $mapper, LoggerInterface $logger)
30
    {
31
        $this->clientService = $clientService;
32
        $this->mapper = $mapper;
33
        $this->logger = $logger;
34
    }
35
36
    /**
37
     * @param string $version
38
     * @return array
39
     * @throws DoesNotExistException
40
     */
41
    public function getChangesForVersion(string $version): array
42
    {
43
        $version = $this->normalizeVersion($version);
44
        $changesInfo = $this->mapper->getChanges($version);
45
        $changesData = json_decode($changesInfo->getData(), true);
46
        if (empty($changesData)) {
47
            throw new DoesNotExistException('Unable to decode changes info');
48
        }
49
        return $changesData;
50
    }
51
52
    /**
53
     * returns a x.y.z form of the provided version. Extra numbers will be
54
     * omitted, missing ones added as zeros.
55
     */
56
    public function normalizeVersion(string $version): string
57
    {
58
        $versionNumbers = array_slice(explode('.', $version), 0, 3);
59
        $versionNumbers[0] = $versionNumbers[0] ?: '0'; // deal with empty input
60
        while (count($versionNumbers) < 3) {
61
            // changelog server expects x.y.z, pad 0 if it is too short
62
            $versionNumbers[] = 0;
63
        }
64
        return implode('.', $versionNumbers);
65
    }
66
67
    /**
68
     * @param string $uri
69
     * @param string $version
70
     * @return array
71
     * @throws \Exception
72
     */
73
    public function check(string $uri, string $version): array
74
    {
75
        try {
76
            $version = $this->normalizeVersion($version);
77
            $changesInfo = $this->mapper->getChanges($version);
78
            if ($changesInfo->getLastCheck() + 1800 > time()) {
79
                return json_decode($changesInfo->getData(), true);
80
            }
81
        } catch (DoesNotExistException $e) {
82
            $changesInfo = new WhatsNewResult();
83
        }
84
85
        $response = $this->queryChangesServer($uri, $changesInfo);
86
87
        switch ($this->evaluateResponse($response)) {
88
            case self::RESPONSE_NO_CONTENT:
89
                return [];
90
            case self::RESPONSE_USE_CACHE:
91
                return json_decode($changesInfo->getData(), true);
92
            case self::RESPONSE_HAS_CONTENT:
93
            default:
94
                $data = $this->extractData($response->getBody());
95
                $changesInfo->setData(json_encode($data));
96
                $changesInfo->setEtag($response->getHeader('Etag'));
97
                $this->cacheResult($changesInfo, $version);
98
99
                return $data;
100
        }
101
    }
102
103
    /**
104
     * @throws \Exception
105
     */
106
    protected function queryChangesServer(string $uri, WhatsNewResult $entry): IResponse
107
    {
108
        $headers = [];
109
        if ($entry->getEtag() !== '') {
110
            $headers['If-None-Match'] = [$entry->getEtag()];
111
        }
112
113
        $entry->setLastCheck(time());
114
        $client = $this->clientService->newClient();
115
        return $client->get($uri, [
116
            'headers' => $headers,
117
        ]);
118
    }
119
120
    protected function evaluateResponse(IResponse $response): int
121
    {
122
        if ($response->getStatusCode() === 304) {
123
            return self::RESPONSE_USE_CACHE;
124
        } elseif ($response->getStatusCode() === 404) {
125
            return self::RESPONSE_NO_CONTENT;
126
        } elseif ($response->getStatusCode() === 200) {
127
            return self::RESPONSE_HAS_CONTENT;
128
        }
129
        return self::RESPONSE_NO_CONTENT;
130
    }
131
132
    protected function extractData($body): array
133
    {
134
        $data = [];
135
        if ($body) {
136
            $loadEntities = libxml_disable_entity_loader(true);
137
            $xml = @simplexml_load_string($body);
138
            libxml_disable_entity_loader($loadEntities);
139
            if ($xml !== false) {
140
                $data['changelogURL'] = (string)$xml->changelog['href'];
141
                $data['whatsNew'] = [];
142
                foreach ($xml->whatsNew as $infoSet) {
143
                    $data['whatsNew'][(string)$infoSet['lang']] = [
144
                        'regular' => (array)$infoSet->regular->item,
145
                        'admin' => (array)$infoSet->admin->item,
146
                    ];
147
                }
148
            } else {
149
                libxml_clear_errors();
150
            }
151
        }
152
        return $data;
153
    }
154
155
    protected function cacheResult(WhatsNewResult $entry, string $version)
156
    {
157
        if ($entry->getVersion() === $version) {
158
            $this->mapper->update($entry);
159
        } else {
160
            $entry->setVersion($version);
161
            $this->mapper->insert($entry);
162
        }
163
    }
164
}