Issues (7)

src/Client.php (1 issue)

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();
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