Client::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 5
c 1
b 0
f 1
nc 1
nop 3
dl 0
loc 8
rs 10
1
<?php
2
3
namespace BabyMarkt\DeepL;
4
5
/**
6
 * Class Client implements a DeepL http Client based on PHP-CURL
7
 */
8
final class Client implements ClientInterface
9
{
10
    const API_URL_SCHEMA = 'https';
11
12
    /**
13
     * API BASE URL without authentication query parameter
14
     * https://api.deepl.com/v2/[resource]
15
     */
16
    const API_URL_BASE_NO_AUTH = '%s://%s/v%s/%s';
17
18
    /**
19
     * DeepL API Version (v2 is default since 2018)
20
     *
21
     * @var integer
22
     */
23
    protected $apiVersion;
24
25
    /**
26
     * DeepL API Auth Key (DeepL Pro access required)
27
     *
28
     * @var string
29
     */
30
    protected $authKey;
31
32
    /**
33
     * cURL resource
34
     *
35
     * @var resource
36
     */
37
    protected $curl;
38
39
    /**
40
     * Hostname of the API (in most cases api.deepl.com)
41
     *
42
     * @var string
43
     */
44
    protected $host;
45
46
    /**
47
     * Maximum number of seconds the query should take
48
     *
49
     * @var int|null
50
     */
51
    protected $timeout = null;
52
53
    /**
54
     * URL of the proxy used to connect to DeepL (if needed)
55
     *
56
     * @var string|null
57
     */
58
    protected $proxy = null;
59
60
    /**
61
     * Credentials for the proxy used to connect to DeepL (username:password)
62
     *
63
     * @var string|null
64
     */
65
    protected $proxyCredentials = null;
66
67
    /**
68
     * DeepL constructor
69
     *
70
     * @param string  $authKey
71
     * @param integer $apiVersion
72
     * @param string  $host
73
     */
74
    public function __construct($authKey, $apiVersion = 2, $host = 'api-free.deepl.com')
75
    {
76
        $this->authKey    = $authKey;
77
        $this->apiVersion = $apiVersion;
78
        $this->host       = $host;
79
        $this->curl       = curl_init();
0 ignored issues
show
Documentation Bug introduced by
It seems like curl_init() can also be of type CurlHandle. However, the property $curl is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
80
81
        curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, 1);
82
    }
83
84
    /**
85
     * DeepL destructor
86
     */
87
    public function __destruct()
88
    {
89
        if ($this->curl && is_resource($this->curl)) {
90
            curl_close($this->curl);
91
        }
92
    }
93
94
    /**
95
     * Make a request to the given URL
96
     *
97
     * @param string $url
98
     * @param string $body
99
     * @param string $method
100
     *
101
     * @return array
102
     *
103
     * @throws DeepLException
104
     */
105
    public function request($url, $body = '', $method = 'POST')
106
    {
107
        switch ($method) {
108
            case 'DELETE':
109
                curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
110
                break;
111
            case 'POST':
112
                curl_setopt($this->curl, CURLOPT_POST, true);
113
                curl_setopt($this->curl, CURLOPT_POSTFIELDS, $body);
114
                break;
115
            case 'GET':
116
            default:
117
                break;
118
        }
119
120
        curl_setopt($this->curl, CURLOPT_URL, $url);
121
122
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
123
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, ['Authorization: DeepL-Auth-Key ' . $this->authKey]);
124
125
        if ($this->proxy !== null) {
126
            curl_setopt($this->curl, CURLOPT_PROXY, $this->proxy);
127
        }
128
129
        if ($this->proxyCredentials !== null) {
130
            curl_setopt($this->curl, CURLOPT_PROXYAUTH, $this->proxyCredentials);
131
        }
132
133
        if ($this->timeout !== null) {
134
            curl_setopt($this->curl, CURLOPT_TIMEOUT, $this->timeout);
135
        }
136
137
        $response = curl_exec($this->curl);
138
139
        if (curl_errno($this->curl)) {
140
            throw new DeepLException('There was a cURL Request Error : ' . curl_error($this->curl));
141
        }
142
        $httpCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
143
144
        return $this->handleResponse($response, $httpCode);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->handleResp...e($response, $httpCode) also could return the type string|true which is incompatible with the documented return type array.
Loading history...
145
    }
146
147
    /**
148
     * Set a proxy to use for querying the DeepL API if needed
149
     *
150
     * @param string $proxy Proxy URL (e.g 'http://proxy-domain.com:3128')
151
     */
152
    public function setProxy($proxy)
153
    {
154
        $this->proxy = $proxy;
155
    }
156
157
    /**
158
     * Set the proxy credentials
159
     *
160
     * @param string $proxyCredentials proxy credentials (using 'username:password' format)
161
     */
162
    public function setProxyCredentials($proxyCredentials)
163
    {
164
        $this->proxyCredentials = $proxyCredentials;
165
    }
166
167
    /**
168
     * Set a timeout for queries to the DeepL API
169
     *
170
     * @param int $timeout Timeout in seconds
171
     */
172
    public function setTimeout($timeout)
173
    {
174
        $this->timeout = $timeout;
175
    }
176
177
    /**
178
     * Creates the Base-Url which all the API-resources have in common.
179
     *
180
     * @param string $resource
181
     * @param bool   $withAuth
182
     *
183
     * @return string
184
     */
185
    public function buildBaseUrl(string $resource = 'translate'): string
186
    {
187
        return sprintf(
188
            self::API_URL_BASE_NO_AUTH,
189
            self::API_URL_SCHEMA,
190
            $this->host,
191
            $this->apiVersion,
192
            $resource
193
        );
194
    }
195
196
    /**
197
     * @param array $paramsArray
198
     *
199
     * @return string
200
     */
201
    public function buildQuery($paramsArray)
202
    {
203
        if (isset($paramsArray['text']) && true === is_array($paramsArray['text'])) {
204
            $text = $paramsArray['text'];
205
            unset($paramsArray['text']);
206
            $textString = '';
207
            foreach ($text as $textElement) {
208
                $textString .= '&text=' . rawurlencode($textElement);
209
            }
210
        }
211
212
        foreach ($paramsArray as $key => $value) {
213
            if (true === is_array($value)) {
214
                $paramsArray[$key] = implode(',', $value);
215
            }
216
        }
217
218
        $body = http_build_query($paramsArray, '', '&');
219
220
        if (isset($textString)) {
221
            $body = $textString . '&' . $body;
222
        }
223
224
        return $body;
225
    }
226
227
    /**
228
     * Handles the different kind of response returned from API, array, string or null
229
     *
230
     * @param $response
231
     * @param $httpCode
232
     *
233
     * @return array|mixed|null
234
     *
235
     * @throws DeepLException
236
     */
237
    private function handleResponse($response, $httpCode)
238
    {
239
        $responseArray = json_decode($response, true);
240
        if (($httpCode === 200 || $httpCode === 204) && is_null($responseArray)) {
241
            return empty($response) ? null : $response;
242
        }
243
244
        if ($httpCode !== 200 && is_array($responseArray) && array_key_exists('message', $responseArray)) {
245
            throw new DeepLException($responseArray['message'], $httpCode);
246
        }
247
248
        if (!is_array($responseArray)) {
249
            throw new DeepLException('The Response seems to not be valid JSON.', $httpCode);
250
        }
251
252
        return $responseArray;
253
    }
254
}
255