Lines 89.65% 52 / 58
Methods 83.33% 5 / 6
Classes 0.00% 0 / 1
Covered by tests of size
Name Lines Methods CRAP
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 getVersions 71.42% 15 / 21 0.00% 0 / 1 3.21
 isVerified 100.00% 16 / 16 100.00% 1 / 1 3
 getVerificationIssues 100.00% 17 / 17 100.00% 1 / 1 4
 setHttpClient 100.00% 1 / 1 100.00% 1 / 1 1
 setRemoteHashes 100.00% 2 / 2 100.00% 1 / 1 1
40class RemoteApiClient
41{
42    private string $apiUrl = 'https://api.phpmyfaq.de/';
43
44    private ?string $remoteHashes = null;
45
46    private HttpClientInterface $httpClient;
47
48    /**
49     * Api constructor.
50     */
51    public function __construct(
52        private readonly Configuration $configuration,
53        private readonly System $system,
54    ) {
55        $this->setHttpClient(HttpClient::create(['max_redirects' => 2, 'timeout' => 30]));
56    }
57
58    /**
59     * Returns the installed, the current available and the next version
60     * as an array.
61     *
62     * @return string[]
63     * @throws Exception|DecodingExceptionInterface|TransportExceptionInterface
64     */
65    public function getVersions(): array
66    {
67        $response = $this->httpClient->request('GET', $this->apiUrl . 'versions');
68
69        if ($response->getStatusCode() === Response::HTTP_OK) {
70            try {
71                $content = $response->toArray();
72                return [
73                    'installed' => $this->configuration->getVersion(),
74                    'stable' => (string) ($content['stable'] ?? ''),
75                    'development' => (string) ($content['development'] ?? ''),
76                    'nightly' => (string) ($content['nightly'] ?? ''),
77                ];
78            } catch (
79                ClientExceptionInterface|RedirectionExceptionInterface|ServerExceptionInterface|TransportExceptionInterface $exception
80            ) {
81                throw new Exception(
82                    'phpMyFAQ Verification API is not available: ' . $exception->getMessage(),
83                    (int) $exception->getCode(),
84                    $exception,
85                );
86            }
87        }
88
89        return [
90            'installed' => $this->configuration->getVersion(),
91            'stable' => 'n/a',
92            'development' => 'n/a',
93            'nightly' => 'n/a',
94        ];
95    }
96
97    /**
98     * Returns true, if an installed version can be verified. Otherwise, false.
99     *
100     * @throws Exception|TransportExceptionInterface|\JsonException
101     */
102    public function isVerified(): bool
103    {
104        $response = $this->httpClient->request('GET', $this->apiUrl . 'verify/' . $this->configuration->getVersion());
105
106        try {
107            $this->remoteHashes = $response->getContent();
108            if (
109                json_decode(json: $this->remoteHashes, associative: null, depth: 512, flags: JSON_THROW_ON_ERROR)
110                instanceof stdClass
111            ) {
112                return is_array(json_decode(
113                    json: $this->remoteHashes,
114                    associative: true,
115                    depth: 512,
116                    flags: JSON_THROW_ON_ERROR,
117                ));
118            }
119        } catch (
120            ClientExceptionInterface|RedirectionExceptionInterface|ServerExceptionInterface|TransportExceptionInterface $exception
121        ) {
122            throw new Exception(
123                'phpMyFAQ Verification API is not available: ' . $exception->getMessage(),
124                (int) $exception->getCode(),
125                $exception,
126            );
127        }
128
129        return false;
130    }
131
132    /**
133     * @return string[]
134     * @throws \JsonException
135     * @throws Exception
136     */
137    public function getVerificationIssues(): array
138    {
139        $localHashes = json_decode(
140            json: $this->system->createHashes(),
141            associative: true,
142            depth: 512,
143            flags: JSON_THROW_ON_ERROR,
144        );
145        $remoteHashes = json_decode(
146            json: (string) $this->remoteHashes,
147            associative: true,
148            depth: 512,
149            flags: JSON_THROW_ON_ERROR,
150        );
151
152        $issues = array_diff(is_array($localHashes) ? $localHashes : [], is_array($remoteHashes) ? $remoteHashes : []);
153
154        $verificationIssues = [];
155        foreach ($issues as $issueKey => $issueValue) {
156            $verificationIssues[$issueKey] = (string) $issueValue;
157        }
158
159        return $verificationIssues;
160    }
161
162    public function setHttpClient(HttpClientInterface $httpClient): void
163    {
164        $this->httpClient = $httpClient;
165    }
166
167    public function setRemoteHashes(?string $remoteHashes): RemoteApiClient
168    {
169        $this->remoteHashes = $remoteHashes;
170        return $this;
171    }
172}