Passed
Pull Request — master (#24)
by
unknown
01:46
created

DeepL   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 285
Duplicated Lines 0 %

Test Coverage

Coverage 87%

Importance

Changes 22
Bugs 3 Features 5
Metric Value
wmc 28
eloc 86
c 22
b 3
f 5
dl 0
loc 285
ccs 87
cts 100
cp 0.87
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A __destruct() 0 4 3
A removeEmptyParams() 0 22 6
A request() 0 24 6
A usage() 0 6 1
A buildBaseUrl() 0 12 1
A languages() 0 7 1
A translate() 0 38 2
B buildQuery() 0 24 7
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
    const API_URL_SCHEMA = 'https';
13
    /**
14
     * API BASE URL
15
     * https://api.deepl.com/v2/[resource]?auth_key=[yourAuthKey]
16
     */
17
    const API_URL_BASE = '%s://%s/v%s/%s?auth_key=%s';
18
19
    /**
20
     * API URL: usage
21
     */
22
    const API_URL_RESOURCE_USAGE = 'usage';
23
24
    /**
25
     * API URL: languages
26
     */
27
    const API_URL_RESOURCE_LANGUAGES = 'languages';
28
29
    /**
30
     * DeepL API Version (v2 is default since 2018)
31
     *
32
     * @var integer
33
     */
34
    protected $apiVersion;
35
36
    /**
37
     * DeepL API Auth Key (DeepL Pro access required)
38
     *
39
     * @var string
40
     */
41
    protected $authKey;
42
43
    /**
44
     * cURL resource
45
     *
46
     * @var resource
47
     */
48
    protected $curl;
49
50
    /**
51
     * Hostname of the API (in most cases api.deepl.com)
52
     *
53
     * @var string
54
     */
55
    protected $host;
56
57
    /**
58
     * DeepL constructor
59
     *
60
     * @param string  $authKey
61
     * @param integer $apiVersion
62
     * @param string  $host
63
     */
64 15
    public function __construct($authKey, $apiVersion = 2, $host = 'api.deepl.com')
65
    {
66 15
        $this->authKey    = $authKey;
67 15
        $this->apiVersion = $apiVersion;
68 15
        $this->host       = $host;
69 15
        $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...
70
71 15
        curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, 1);
72 15
    }
73
74
    /**
75
     * DeepL destructor
76
     */
77 15
    public function __destruct()
78
    {
79 15
        if ($this->curl && is_resource($this->curl)) {
80 15
            curl_close($this->curl);
81 15
        }
82 15
    }
83
84
    /**
85
     * Call languages-Endpoint and return Json-response as an Array
86
     *
87
     * @param string $type
88
     *
89
     * @return array
90
     * @throws DeepLException
91
     */
92
    public function languages($type = null)
93
    {
94
        $url       = $this->buildBaseUrl(self::API_URL_RESOURCE_LANGUAGES);
95
        $body      = $this->buildQuery(array('type' => $type));
96
        $languages = $this->request($url, $body);
97
98
        return $languages;
99
    }
100
101
    /**
102
     * Translate the text string or array from source to destination language
103
     * For detailed info on Parameters see README.md
104
     *
105
     * @param string|string[] $text
106
     * @param string          $sourceLang
107
     * @param string          $targetLang
108
     * @param string          $tagHandling
109
     * @param array|null      $ignoreTags
110
     * @param string          $formality
111
     * @param null            $splitSentences
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $splitSentences is correct as it would always require null to be passed?
Loading history...
112
     * @param null            $preserveFormatting
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $preserveFormatting is correct as it would always require null to be passed?
Loading history...
113
     * @param array|null      $nonSplittingTags
114
     * @param null            $outlineDetection
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $outlineDetection is correct as it would always require null to be passed?
Loading history...
115
     * @param array|null      $splittingTags
116
     *
117
     * @return array
118
     * @throws DeepLException
119
     *
120
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
121
     */
122 2
    public function translate(
123
        $text,
124
        $sourceLang = 'de',
125
        $targetLang = 'en',
126
        $tagHandling = null,
127
        array $ignoreTags = null,
128
        $formality = 'default',
129
        $splitSentences = null,
130
        $preserveFormatting = null,
131
        array $nonSplittingTags = null,
132
        $outlineDetection = null,
133
        array $splittingTags = null
134
    ) {
135 2
        if (is_array($tagHandling)) {
0 ignored issues
show
introduced by
The condition is_array($tagHandling) is always false.
Loading history...
136 1
            throw new \InvalidArgumentException('$tagHandling must be of type String in V2 of DeepLLibrary');
137
        }
138
        $paramsArray = array(
139 1
            'text'                => $text,
140 1
            'source_lang'         => $sourceLang,
141 1
            'target_lang'         => $targetLang,
142 1
            'splitting_tags'      => $splittingTags,
143 1
            'non_splitting_tags'  => $nonSplittingTags,
144 1
            'ignore_tags'         => $ignoreTags,
145 1
            'tag_handling'        => $tagHandling,
146 1
            'formality'           => $formality,
147 1
            'split_sentences'     => $splitSentences,
148 1
            'preserve_formatting' => $preserveFormatting,
149 1
            'outline_detection'   => $outlineDetection,
150 1
        );
151
152 1
        $paramsArray = $this->removeEmptyParams($paramsArray);
153 1
        $url         = $this->buildBaseUrl();
154 1
        $body        = $this->buildQuery($paramsArray);
155
156
        // request the DeepL API
157 1
        $translationsArray = $this->request($url, $body);
158
159
        return $translationsArray['translations'];
160
    }
161
162
    /**
163
     * Calls the usage-Endpoint and return Json-response as an array
164
     *
165
     * @return array
166
     * @throws DeepLException
167
     */
168
    public function usage()
169
    {
170
        $url   = $this->buildBaseUrl(self::API_URL_RESOURCE_USAGE);
171
        $usage = $this->request($url);
172
173
        return $usage;
174
    }
175
176
177
    /**
178
     * Creates the Base-Url which all of the 3 API-resources have in common.
179
     *
180
     * @param string $resource
181
     *
182
     * @return string
183
     */
184 3
    protected function buildBaseUrl($resource = 'translate')
185
    {
186 3
        $url = sprintf(
187 3
            self::API_URL_BASE,
188 3
            self::API_URL_SCHEMA,
189 3
            $this->host,
190 3
            $this->apiVersion,
191 3
            $resource,
192 3
            $this->authKey
193 3
        );
194
195 3
        return $url;
196
    }
197
198
    /**
199
     * @param array $paramsArray
200
     *
201
     * @return string
202
     */
203 7
    protected function buildQuery($paramsArray)
204
    {
205 7
        if (isset($paramsArray['text']) && true === is_array($paramsArray['text'])) {
206 1
            $text = $paramsArray['text'];
207 1
            unset($paramsArray['text']);
208 1
            $textString = '';
209 1
            foreach ($text as $textElement) {
210 1
                $textString .= '&text='.rawurlencode($textElement);
211 1
            }
212 1
        }
213
214 7
        foreach ($paramsArray as $key => $value) {
215 7
            if (true === is_array($value)) {
216 4
                $paramsArray[$key] = implode(',', $value);
217 4
            }
218 7
        }
219
220 7
        $body = http_build_query($paramsArray, null, '&');
0 ignored issues
show
Bug introduced by
null of type null is incompatible with the type string expected by parameter $numeric_prefix of http_build_query(). ( Ignorable by Annotation )

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

220
        $body = http_build_query($paramsArray, /** @scrutinizer ignore-type */ null, '&');
Loading history...
221
222 7
        if (isset($textString)) {
223 1
            $body = $textString.'&'.$body;
224 1
        }
225
226 7
        return $body;
227
    }
228
229
230
231
232
    /**
233
     * Make a request to the given URL
234
     *
235
     * @param string $url
236
     * @param string $body
237
     *
238
     * @return array
239
     *
240
     * @throws DeepLException
241
     */
242 1
    protected function request($url, $body = '')
243
    {
244 1
        curl_setopt($this->curl, CURLOPT_POST, true);
245 1
        curl_setopt($this->curl, CURLOPT_URL, $url);
246 1
        curl_setopt($this->curl, CURLOPT_POSTFIELDS, $body);
247 1
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
248
249 1
        $response = curl_exec($this->curl);
250
251 1
        if (curl_errno($this->curl)) {
252
            throw new DeepLException('There was a cURL Request Error.');
253
        }
254 1
        $httpCode      = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
255 1
        $responseArray = json_decode($response, true);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type true; however, parameter $json of json_decode() does only seem to accept string, 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

255
        $responseArray = json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
256
257 1
        if ($httpCode != 200 && is_array($responseArray) && array_key_exists('message', $responseArray)) {
258
            throw new DeepLException($responseArray['message'], $httpCode);
259
        }
260
261 1
        if (false === is_array($responseArray)) {
262 1
            throw new DeepLException('The Response seems to not be valid JSON.', $httpCode);
263
        }
264
265
        return $responseArray;
266
    }
267
268
    /**
269
     * @param array $paramsArray
270
     *
271
     * @return array
272
     */
273 6
    private function removeEmptyParams($paramsArray)
274
    {
275
276 6
        foreach ($paramsArray as $key => $value) {
277 6
            if (true === empty($value)) {
278 6
                unset($paramsArray[$key]);
279 6
            }
280
            // Special Workaround for outline_detection which will be unset above
281
            // DeepL assumes outline_detection=1 if it is not send
282
            // in order to deactivate it, we need to send outline_detection=0 to the api
283 6
            if ('outline_detection' === $key) {
284 6
                if (1 === $value) {
285 1
                    unset($paramsArray[$key]);
286 1
                }
287
288 6
                if (0 === $value) {
289 2
                    $paramsArray[$key] = 0;
290 2
                }
291 6
            }
292 6
        }
293
294 6
        return $paramsArray;
295
    }
296
}
297