Completed
Push — master ( 1770d2...3ec0f3 )
by
unknown
16s queued 12s
created

DeepL::buildQuery()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 7

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 13
c 3
b 1
f 0
dl 0
loc 24
ccs 14
cts 14
cp 1
rs 8.8333
cc 7
nc 12
nop 1
crap 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 31
    public function __construct($authKey, $apiVersion = 2, $host = 'api.deepl.com')
65
    {
66 31
        $this->authKey    = $authKey;
67 31
        $this->apiVersion = $apiVersion;
68 31
        $this->host       = $host;
69 31
        $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...
70
71 31
        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

71
        curl_setopt(/** @scrutinizer ignore-type */ $this->curl, CURLOPT_RETURNTRANSFER, 1);
Loading history...
72 31
    }
73
74
    /**
75
     * DeepL destructor
76
     */
77 31
    public function __destruct()
78
    {
79 31
        if ($this->curl && is_resource($this->curl)) {
80 31
            curl_close($this->curl);
81
        }
82 31
    }
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 4
    public function languages($type = null)
93
    {
94 4
        $url       = $this->buildBaseUrl(self::API_URL_RESOURCE_LANGUAGES);
95 4
        $body      = $this->buildQuery(array('type' => $type));
96 4
        $languages = $this->request($url, $body);
97
98 3
        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 13
    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 13
        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 12
            'text'                => $text,
140 12
            'source_lang'         => $sourceLang,
141 12
            'target_lang'         => $targetLang,
142 12
            'splitting_tags'      => $splittingTags,
143 12
            'non_splitting_tags'  => $nonSplittingTags,
144 12
            'ignore_tags'         => $ignoreTags,
145 12
            'tag_handling'        => $tagHandling,
146 12
            'formality'           => $formality,
147 12
            'split_sentences'     => $splitSentences,
148 12
            'preserve_formatting' => $preserveFormatting,
149 12
            'outline_detection'   => $outlineDetection,
150
        );
151
152 12
        $paramsArray = $this->removeEmptyParams($paramsArray);
153 12
        $url         = $this->buildBaseUrl();
154 12
        $body        = $this->buildQuery($paramsArray);
155
156
        // request the DeepL API
157 12
        $translationsArray = $this->request($url, $body);
158
159 7
        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 1
    public function usage()
169
    {
170 1
        $url   = $this->buildBaseUrl(self::API_URL_RESOURCE_USAGE);
171 1
        $usage = $this->request($url);
172
173 1
        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 19
    protected function buildBaseUrl($resource = 'translate')
185
    {
186 19
        $url = sprintf(
187 19
            self::API_URL_BASE,
188 19
            self::API_URL_SCHEMA,
189 19
            $this->host,
190 19
            $this->apiVersion,
191 19
            $resource,
192 19
            $this->authKey
193
        );
194
195 19
        return $url;
196
    }
197
198
    /**
199
     * @param array $paramsArray
200
     *
201
     * @return string
202
     */
203 22
    protected function buildQuery($paramsArray)
204
    {
205 22
        if (isset($paramsArray['text']) && true === is_array($paramsArray['text'])) {
206 2
            $text = $paramsArray['text'];
207 2
            unset($paramsArray['text']);
208 2
            $textString = '';
209 2
            foreach ($text as $textElement) {
210 2
                $textString .= '&text='.rawurlencode($textElement);
211
            }
212
        }
213
214 22
        foreach ($paramsArray as $key => $value) {
215 22
            if (true === is_array($value)) {
216 22
                $paramsArray[$key] = implode(',', $value);
217
            }
218
        }
219
220 22
        $body = http_build_query($paramsArray, null, '&');
221
222 22
        if (isset($textString)) {
223 2
            $body = $textString.'&'.$body;
224
        }
225
226 22
        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 17
    protected function request($url, $body = '')
243
    {
244 17
        curl_setopt($this->curl, CURLOPT_POST, true);
245 17
        curl_setopt($this->curl, CURLOPT_URL, $url);
246 17
        curl_setopt($this->curl, CURLOPT_POSTFIELDS, $body);
247 17
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
248
249 17
        $response = curl_exec($this->curl);
250
251 17
        if (curl_errno($this->curl)) {
252
            throw new DeepLException('There was a cURL Request Error.');
253
        }
254 17
        $httpCode      = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
255 17
        $responseArray = json_decode($response, true);
256
257 17
        if ($httpCode != 200 && is_array($responseArray) && array_key_exists('message', $responseArray)) {
258 4
            throw new DeepLException($responseArray['message'], $httpCode);
259
        }
260
261 13
        if (false === is_array($responseArray)) {
262 2
            throw new DeepLException('The Response seems to not be valid JSON.', $httpCode);
263
        }
264
265 11
        return $responseArray;
266
    }
267
268
    /**
269
     * @param array $paramsArray
270
     *
271
     * @return array
272
     */
273 17
    private function removeEmptyParams($paramsArray)
274
    {
275
276 17
        foreach ($paramsArray as $key => $value) {
277 17
            if (true === empty($value)) {
278 17
                unset($paramsArray[$key]);
279
            }
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 17
            if ('outline_detection' === $key) {
284 17
                if (1 === $value) {
285 2
                    unset($paramsArray[$key]);
286
                }
287
288 17
                if (0 === $value) {
289 17
                    $paramsArray[$key] = 0;
290
                }
291
            }
292
        }
293
294 17
        return $paramsArray;
295
    }
296
}
297