Passed
Pull Request — master (#12)
by
unknown
01:57
created

DeepL::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 4
c 1
b 0
f 1
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 10
cc 1
nc 1
nop 2
crap 1
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
    );
81
82
    /**
83
     * Supported translation destination languages
84
     *
85
     * @var array
86
     */
87
    protected $destinationLanguages = array(
88
        'EN',
89
        'DE',
90
        'FR',
91
        'ES',
92
        'PT',
93
        'PT-PT',
94
        'PT-BR',
95
        'IT',
96
        'NL',
97
        'PL',
98
        'RU'
99
    );
100
101
    /**
102
     * @var integer
103
     */
104
    protected $apiVersion;
105
106
    /**
107
     * DeepL API Auth Key (DeepL Pro access required)
108
     *
109
     * @var string
110
     */
111
    protected $authKey;
112
113
    /**
114
     * cURL resource
115
     *
116
     * @var resource
117
     */
118
    protected $curl;
119
120
    /**
121
     * DeepL constructor
122
     *
123
     * @param string  $authKey
124
     * @param integer $apiVersion
125
     */
126 7
    public function __construct($authKey, $apiVersion = 1)
127
    {
128 7
        $this->authKey    = $authKey;
129 7
        $this->apiVersion = $apiVersion;
130 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...
131
132 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

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