Passed
Push — dev ( 353ead...314070 )
by Mattia
05:17
created

MojangClient::sendApiRequest()   A

Complexity

Conditions 3
Paths 7

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 14
nc 7
nop 2
dl 0
loc 19
rs 9.7998
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Minecraft;
6
7
use GuzzleHttp\Client as HttpClient;
8
use GuzzleHttp\Exception\BadResponseException;
9
10
/**
11
 * Class MojangClient.
12
 */
13
class MojangClient
14
{
15
    /**
16
     * User Agent used for requests.
17
     */
18
    private const USER_AGENT = 'Minepic/2.0 (minepic.org)';
19
20
    /**
21
     * HTTP Client for requests.
22
     *
23
     * @var \GuzzleHttp\Client
24
     */
25
    private \GuzzleHttp\Client $httpClient;
26
27
    /**
28
     * Last API Response.
29
     *
30
     * @var
31
     */
32
    private $lastResponse;
33
34
    /**
35
     * Last Error.
36
     *
37
     * @var string
38
     */
39
    private string $lastError = '';
40
41
    /**
42
     * Last error code.
43
     *
44
     * @var int
45
     */
46
    private int $lastErrorCode = 0;
47
48
    /**
49
     * Last content Type.
50
     *
51
     * @var string
52
     */
53
    private string $lastContentType = '';
54
55
    /**
56
     * MojangClient constructor.
57
     */
58
    public function __construct()
59
    {
60
        $this->httpClient = new HttpClient(
61
            [
62
                'headers' => [
63
                    'User-Agent' => self::USER_AGENT,
64
                ],
65
            ]
66
        );
67
    }
68
69
    /**
70
     * Last response from API.
71
     *
72
     * @return mixed
73
     */
74
    public function getLastResponse()
75
    {
76
        return $this->lastResponse;
77
    }
78
79
    private function handleGuzzleBadResponseException(
80
        BadResponseException $badResponseException
81
    ): void {
82
        $this->lastContentType = $badResponseException->getResponse()->getHeader('content-type')[0] ?? '';
83
        $this->lastErrorCode = $badResponseException->getResponse()->getStatusCode();
84
        $this->lastError = 'Error';
85
        if (isset($this->lastResponse['errorMessage'])) {
86
            $this->lastError .= ': '.$this->lastResponse['errorMessage'];
87
        }
88
    }
89
90
    private function handleThrowable(\Throwable $exception): void
91
    {
92
        $this->lastContentType = '';
93
        $this->lastErrorCode = 0;
94
        $this->lastError = $exception->getFile().':'.$exception->getLine().' - '.$exception->getMessage();
95
    }
96
97
    /**
98
     * Send new request.
99
     *
100
     * @param string $method HTTP Verb
101
     * @param string $url API Endpoint
102
     * @return bool
103
     */
104
    private function sendApiRequest(string $method, string $url): bool
105
    {
106
        try {
107
            $response = $this->httpClient->request($method, $url);
108
            $this->lastResponse = json_decode($response->getBody()->getContents(), true);
109
            $this->lastContentType = $response->getHeader('content-type')[0];
110
            $this->lastErrorCode = 0;
111
            $this->lastError = '';
112
113
            return true;
114
        } catch (BadResponseException $exception) {
115
            $this->lastResponse = json_decode($exception->getResponse()->getBody()->getContents(), true);
116
            $this->handleGuzzleBadResponseException($exception);
117
118
            return false;
119
        } catch (\Throwable $exception) {
120
            $this->handleThrowable($exception);
121
122
            return false;
123
        }
124
    }
125
126
    /**
127
     * Generic request.
128
     * @param string $method
129
     * @param string $url
130
     * @return bool
131
     */
132
    private function sendRequest(string $method, string $url): bool
133
    {
134
        try {
135
            $response = $this->httpClient->request($method, $url);
136
            $this->lastResponse = $response->getBody()->getContents();
137
            $this->lastContentType = $response->getHeader('content-type')[0];
138
            $this->lastErrorCode = 0;
139
            $this->lastError = '';
140
141
            return true;
142
        } catch (BadResponseException $exception) {
143
            $this->lastResponse = $exception->getResponse()->getBody()->getContents();
144
            $this->handleGuzzleBadResponseException($exception);
145
146
            return false;
147
        } catch (\Throwable $exception) {
148
            $this->handleThrowable($exception);
149
150
            return false;
151
        }
152
    }
153
154
    /**
155
     * Account info from username.
156
     *
157
     * @param string $username
158
     * @return MojangAccount
159
     * @throws \Exception
160
     */
161
    public function sendUsernameInfoRequest(string $username): MojangAccount
162
    {
163
        if ($this->sendApiRequest('GET', env('MINECRAFT_PROFILE_URL').$username)) {
164
            return new MojangAccount([
165
                'username' => $this->lastResponse['name'],
166
                'uuid' => $this->lastResponse['id'],
167
            ]);
168
        }
169
        throw new \Exception($this->lastError, $this->lastErrorCode);
170
    }
171
172
    /**
173
     * Account info from UUID.
174
     *
175
     * @param string $uuid User UUID
176
     * @return MojangAccount
177
     * @throws \Exception
178
     */
179
    public function getUuidInfo(string $uuid): MojangAccount
180
    {
181
        if ($this->sendApiRequest('GET', env('MINECRAFT_SESSION_URL').$uuid)) {
182
            $account = new MojangAccount();
183
            if ($account->loadFromApiResponse($this->lastResponse)) {
184
                return $account;
185
            }
186
            throw new \Exception('Cannot create data account');
187
        }
188
        throw new \Exception($this->lastError, $this->lastErrorCode);
189
    }
190
191
    /**
192
     * Get Skin.
193
     *
194
     * @param string $skin Skin uuid
195
     * @throws \Exception
196
     */
197
    public function getSkin(string $skin)
198
    {
199
        if ($this->sendRequest('GET', env('MINECRAFT_TEXTURE_URL').$skin)) {
200
            if ($this->lastContentType === 'image/png') {
201
                return $this->lastResponse;
202
            }
203
            throw new \Exception('Invalid format: ');
204
        }
205
        throw new \Exception($this->lastError, $this->lastErrorCode);
206
    }
207
}
208