Completed
Push — master ( d70e3a...6009d8 )
by
unknown
16s queued 12s
created

DeepL::__destruct()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 3
nc 2
nop 0
crap 3
1
<?php
2
3
namespace BabyMarkt\DeepL;
4
5
/**
6
 * DeepL API client library
7
 *
8
 * @package BabyMarkt\DeepL
9
 */
10
class DeepL
11
{
12
    /**
13
     * API v1 URL
14
     */
15
    const API_URL_V1               = 'https://api.deepl.com/v1/translate';
16
17
    /**
18
     * API v2 URL
19
     */
20
    const API_URL_V2               = 'https://api.deepl.com/v2/translate';
21
22
    /**
23
     * API URL: Parameter auth_key
24
     */
25
    const API_URL_AUTH_KEY         = 'auth_key=%s';
26
27
    /**
28
     * API URL: Parameter text
29
     */
30
    const API_URL_TEXT             = 'text=%s';
31
32
    /**
33
     * API URL: Parameter source_lang
34
     */
35
    const API_URL_SOURCE_LANG      = 'source_lang=%s';
36
37
    /**
38
     * API URL: Parameter target_lang
39
     */
40
    const API_URL_DESTINATION_LANG = 'target_lang=%s';
41
42
    /**
43
     * API URL: Parameter tag_handling
44
     */
45
    const API_URL_TAG_HANDLING     = 'tag_handling=%s';
46
47
    /**
48
     * API URL: Parameter ignore_tags
49
     */
50
    const API_URL_IGNORE_TAGS      = 'ignore_tags=%s';
51
52
    /**
53
     * DeepL HTTP error codes
54
     *
55
     * @var array
56
     */
57
    protected $errorCodes = array(
58
        400 => 'Wrong request, please check error message and your parameters.',
59
        403 => 'Authorization failed. Please supply a valid auth_key parameter.',
60
        413 => 'Request Entity Too Large. The request size exceeds the current limit.',
61
        429 => 'Too many requests. Please wait and send your request once again.',
62
        456 => 'Quota exceeded. The character limit has been reached.'
63
    );
64
65
    /**
66
     * Supported translation source languages
67
     *
68
     * @var array
69
     */
70
    protected $sourceLanguages = array(
71
        'EN',
72
        'DE',
73
        'FR',
74
        'ES',
75
        'PT',
76
        'IT',
77
        'NL',
78
        'PL',
79
        'RU',
80
        'JA',
81
        'ZH'
82
    );
83
84
    /**
85
     * Supported translation destination languages
86
     *
87
     * @var array
88
     */
89
    protected $destinationLanguages = array(
90
        'EN',
91
        'DE',
92
        'FR',
93
        'ES',
94
        'PT',
95
        'IT',
96
        'NL',
97
        'PL',
98
        'RU',
99
        'JA',
100
        'ZH'
101
    );
102
103
    /**
104
     * @var integer
105
     */
106
    protected $apiVersion;
107
108
    /**
109
     * DeepL API Auth Key (DeepL Pro access required)
110
     *
111
     * @var string
112
     */
113
    protected $authKey;
114
115
    /**
116
     * cURL resource
117
     *
118
     * @var resource
119
     */
120
    protected $curl;
121
122
    /**
123
     * DeepL constructor
124
     *
125
     * @param string  $authKey
126
     * @param integer $apiVersion
127
     */
128 7
    public function __construct($authKey, $apiVersion = 1)
129
    {
130 7
        $this->authKey    = $authKey;
131 7
        $this->apiVersion = $apiVersion;
132 7
        $this->curl       = curl_init();
0 ignored issues
show
Documentation Bug introduced by
It seems like curl_init() can also be of type false. 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...
133
134 7
        curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, 1);
0 ignored issues
show
Bug introduced by
It seems like $this->curl can also be of type false; however, parameter $ch of curl_setopt() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

134
        curl_setopt(/** @scrutinizer ignore-type */ $this->curl, CURLOPT_RETURNTRANSFER, 1);
Loading history...
135 7
    }
136
137
    /**
138
     * DeepL destructor
139
     */
140 7
    public function __destruct()
141
    {
142 7
        if ($this->curl && is_resource($this->curl)) {
143 7
            curl_close($this->curl);
144 7
        }
145 7
    }
146
147
    /**
148
     * Translate the text string or array from source to destination language
149
     *
150
     * @param string|string[] $text
151
     * @param string          $sourceLanguage
152
     * @param string          $destinationLanguage
153
     * @param array           $tagHandling
154
     * @param array           $ignoreTags
155
     *
156
     * @return string|string[]
157
     *
158
     * @throws DeepLException
159
     */
160 1
    public function translate(
161
        $text,
162
        $sourceLanguage = 'de',
163
        $destinationLanguage = 'en',
164
        array $tagHandling = array(),
165
        array $ignoreTags = array()
166
    ) {
167
        // make sure we only accept supported languages
168 1
        $this->checkLanguages($sourceLanguage, $destinationLanguage);
169
170
        // build the DeepL API request url
171 1
        $url  = $this->buildUrl($sourceLanguage, $destinationLanguage, $tagHandling, $ignoreTags);
172 1
        $body = $this->buildBody($text);
173
174
        // request the DeepL API
175 1
        $translationsArray = $this->request($url, $body);
176
        $translationsCount = count($translationsArray['translations']);
177
178
        if ($translationsCount == 0) {
179
            throw new DeepLException('No translations found.');
180
        } elseif ($translationsCount == 1) {
181
            return $translationsArray['translations'][0]['text'];
182
        }
183
184
        return $translationsArray['translations'];
185
    }
186
187
    /**
188
     * Check if the given languages are supported
189
     *
190
     * @param string $sourceLanguage
191
     * @param string $destinationLanguage
192
     *
193
     * @return boolean
194
     *
195
     * @throws DeepLException
196
     */
197 4
    protected function checkLanguages($sourceLanguage, $destinationLanguage)
198
    {
199 4
        $sourceLanguage = strtoupper($sourceLanguage);
200
201 4
        if (!in_array($sourceLanguage, $this->sourceLanguages)) {
202 1
            throw new DeepLException(
203 1
                sprintf('The language "%s" is not supported as source language.', $sourceLanguage)
204 1
            );
205
        }
206
207 3
        $destinationLanguage = strtoupper($destinationLanguage);
208
209 3
        if (!in_array($destinationLanguage, $this->destinationLanguages)) {
210 1
            throw new DeepLException(
211 1
                sprintf('The language "%s" is not supported as destination language.', $destinationLanguage)
212 1
            );
213
        }
214
215 2
        return true;
216
    }
217
218
    /**
219
     * Build the URL for the DeepL API request
220
     *
221
     * @param string $sourceLanguage
222
     * @param string $destinationLanguage
223
     * @param array  $tagHandling
224
     * @param array  $ignoreTags
225
     *
226
     * @return string
227
     */
228 3
    protected function buildUrl(
229
        $sourceLanguage,
230
        $destinationLanguage,
231
        array $tagHandling = array(),
232
        array $ignoreTags = array()
233
    ) {
234
        // select correct api url
235 3
        switch ($this->apiVersion) {
236 3
            case 1:
237 3
                $url = DeepL::API_URL_V1;
238 3
                break;
239
            case 2:
240
                $url = DeepL::API_URL_V2;
241
                break;
242
            default:
243
                $url = DeepL::API_URL_V1;
244 3
        }
245
246 3
        $url .= '?' . sprintf(DeepL::API_URL_AUTH_KEY, $this->authKey);
247 3
        $url .= '&' . sprintf(DeepL::API_URL_SOURCE_LANG, strtolower($sourceLanguage));
248 3
        $url .= '&' . sprintf(DeepL::API_URL_DESTINATION_LANG, strtolower($destinationLanguage));
249
250 3
        if (!empty($tagHandling)) {
251 1
            $url .= '&' . sprintf(DeepL::API_URL_TAG_HANDLING, implode(',', $tagHandling));
252 1
        }
253
254 3
        if (!empty($ignoreTags)) {
255 1
            $url .= '&' . sprintf(DeepL::API_URL_IGNORE_TAGS, implode(',', $ignoreTags));
256 1
        }
257
258 3
        return $url;
259
    }
260
261
    /**
262
     * Build the body for the DeepL API request
263
     *
264
     * @param string|string[] $text
265
     *
266
     * @return string
267
     */
268 2
    protected function buildBody($text)
269
    {
270 2
        $body  = '';
271 2
        $first = true;
272
273 2
        if (!is_array($text)) {
274 2
            $text = (array)$text;
275 2
        }
276
277 2
        foreach ($text as $textElement) {
278 2
            $body .= ($first ? '' : '&') . sprintf(DeepL::API_URL_TEXT, rawurlencode($textElement));
279
280 2
            if ($first) {
281 2
                $first = false;
282 2
            }
283 2
        }
284
285 2
        return $body;
286
    }
287
288
    /**
289
     * Make a request to the given URL
290
     *
291
     * @param string $url
292
     *
293
     * @return array
294
     *
295
     * @throws DeepLException
296
     */
297 1
    protected function request($url, $body)
298
    {
299 1
        curl_setopt($this->curl, CURLOPT_URL, $url);
300 1
        curl_setopt($this->curl, CURLOPT_POSTFIELDS, $body);
301 1
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
302
303 1
        $response = curl_exec($this->curl);
304
305 1
        if (curl_errno($this->curl)) {
306
            throw new DeepLException('There was a cURL Request Error.');
307
        }
308
309 1
        $httpCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
310
311 1
        if ($httpCode != 200 && array_key_exists($httpCode, $this->errorCodes)) {
312 1
            throw new DeepLException($this->errorCodes[$httpCode], $httpCode);
313
        }
314
315
        $translationsArray = json_decode($response, true);
316
317
        if (!$translationsArray) {
318
            throw new DeepLException('The Response seems to not be valid JSON.');
319
        }
320
321
        return $translationsArray;
322
    }
323
}
324