Completed
Push — master ( 7cf16c...46457b )
by Byron
05:31
created

MicrosoftTranslator   B

Complexity

Total Complexity 22

Size/Duplication

Total Lines 234
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 16

Test Coverage

Coverage 100%

Importance

Changes 16
Bugs 1 Features 1
Metric Value
wmc 22
c 16
b 1
f 1
lcom 1
cbo 16
dl 0
loc 234
ccs 73
cts 73
cp 1
rs 8.4614

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 2
A setClient() 0 5 1
A getAccessToken() 0 23 3
A createRequest() 0 17 1
A execute() 0 19 3
A assertNoTranslateExceptionAndZeroBalance() 0 8 3
A assertNoArgumentException() 0 10 3
A translate() 0 4 1
A detect() 0 4 1
A speak() 0 4 1
A getLanguagesForSpeak() 0 4 1
A getLanguageNames() 0 4 1
A getLanguagesForTranslate() 0 4 1
1
<?php
2
/**
3
 * This file is part of the badams\MicrosoftTranslator library
4
 *
5
 * @license http://opensource.org/licenses/MIT
6
 * @link https://github.com/badams/microsoft-translator
7
 * @package badams/microsoft-translator
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace badams\MicrosoftTranslator;
14
15
use badams\MicrosoftTranslator\Exceptions\ArgumentException;
16
use badams\MicrosoftTranslator\Exceptions\AuthException;
17
use badams\MicrosoftTranslator\Exceptions\QuotaExceededException;
18
use badams\MicrosoftTranslator\Exceptions\TokenExpiredException;
19
use badams\MicrosoftTranslator\Exceptions\TranslatorException;
20
use badams\MicrosoftTranslator\Methods\Detect;
21
use badams\MicrosoftTranslator\Methods\GetLanguageNames;
22
use badams\MicrosoftTranslator\Methods\GetLanguagesForSpeak;
23
use badams\MicrosoftTranslator\Methods\GetLanguagesForTranslate;
24
use badams\MicrosoftTranslator\Methods\Speak;
25
use badams\MicrosoftTranslator\Methods\Translate;
26
use GuzzleHttp\Exception\RequestException;
27
28
/**
29
 * Class MicrosoftTranslator
30
 * @package badams\MicrosoftTranslator
31
 */
32
class MicrosoftTranslator
33
{
34
    /**
35
     *
36
     */
37
    const AUTH_URL = 'https://datamarket.accesscontrol.windows.net/v2/OAuth2-13';
38
39
    /**
40
     *
41
     */
42
    const BASE_URL = 'http://api.microsofttranslator.com/V2/Http.svc/';
43
44
    /**
45
     * @var \GuzzleHttp\ClientInterface
46
     */
47
    private $http;
48
49
    /**
50
     * @var string
51
     */
52
    private $scope = 'http://api.microsofttranslator.com';
53
54
    /**
55
     * @var string
56
     */
57
    private $grantType = 'client_credentials';
58
59
    /**
60
     * @var string
61
     */
62
    private $clientId;
63
64
    /**
65
     * @var string
66
     */
67
    private $clientSecret;
68
69
    /**
70
     * @var string
71
     */
72
    private $accessToken;
73
74
    /**
75
     * MicrosoftTranslator constructor.
76
     */
77 42
    public function __construct(\GuzzleHttp\ClientInterface $httpClient = null)
78
    {
79 42
        if (is_null($httpClient)) {
80 3
            $httpClient = new \GuzzleHttp\Client();
81 3
        }
82
83 42
        $this->http = $httpClient;
84 42
    }
85
86
    /**
87
     * @param $id
88
     * @param $secret
89
     */
90 27
    public function setClient($id, $secret)
91
    {
92 27
        $this->clientId = $id;
93 27
        $this->clientSecret = $secret;
94 27
    }
95
96
    /**
97
     * @return mixed
98
     * @throws AuthException
99
     */
100 36
    private function getAccessToken()
101
    {
102 36
        if (!$this->accessToken) {
103 36
            $params = array_merge([
104 36
                'grant_type' => $this->grantType,
105 36
                'scope' => $this->scope,
106 36
                'client_id' => $this->clientId,
107 36
                'client_secret' => $this->clientSecret
108 36
            ]);
109
110
            try {
111 36
                $response = $this->http->post(self::AUTH_URL, ['body' => $params]);
112 33
                $result = json_decode((string)$response->getBody());
113 36
            } catch (RequestException $e) {
114 3
                $result = json_decode((string)$e->getResponse()->getBody());
115 3
                throw new AuthException($result->error_description);
116
            }
117
118 33
            $this->accessToken = $result->access_token;
119 33
        }
120
121 33
        return $this->accessToken;
122
    }
123
124
    /**
125
     * @param ApiMethodInterface $method
126
     * @return \GuzzleHttp\Message\Request|\GuzzleHttp\Message\RequestInterface
127
     * @throws AuthException
128
     */
129 33
    private function createRequest(ApiMethodInterface $method)
130
    {
131 33
        $reflection = new \ReflectionClass($method);
132
133 33
        return $this->http->createRequest(
134 33
            $method->getRequestMethod(),
135 33
            self::BASE_URL . $reflection->getShortName(),
136 33
            array_merge([
137 33
                'exceptions' => false,
138
                'headers' => [
139 33
                    'Authorization' => 'Bearer ' . $this->getAccessToken(),
140 33
                    'Content-Type' => 'text/xml',
141 33
                ],
142
143 33
            ], $method->getRequestOptions())
144 33
        );
145
    }
146
147
    /**
148
     * @param ApiMethodInterface $method
149
     * @return mixed
150
     * @throws ArgumentException
151
     * @throws QuotaExceededException
152
     * @throws TranslatorException
153
     */
154 33
    private function execute(ApiMethodInterface $method)
155
    {
156 33
        $response = $this->http->send($this->createRequest($method));
157
158 33
        if ($response->getStatusCode() != 200) {
159
            try {
160 12
                $message = strip_tags($response->getBody());
161 12
                $this->assertNoArgumentException($message);
162 6
                $this->assertNoTranslateExceptionAndZeroBalance($message);
163 12
            } catch (TokenExpiredException $e) {
164 3
                $this->accessToken = null;
165 3
                return $this->execute($method);
166
            }
167
168 3
            throw new TranslatorException($response->getBody());
169
        }
170
171 24
        return $method->processResponse($response);
172
    }
173
174
    /**
175
     * @param string $message
176
     * @throws QuotaExceededException
177
     */
178 6
    private function assertNoTranslateExceptionAndZeroBalance($message)
179
    {
180 6
        if (strpos($message, 'TranslateApiException') === 0
181 6
            && strpos($message, 'credentials has zero balance.')
182 6
        ) {
183 3
            throw new QuotaExceededException($message);
184
        }
185 3
    }
186
187
    /**
188
     * @param string $message
189
     * @throws ArgumentException
190
     * @throws TokenExpiredException
191
     */
192 12
    private function assertNoArgumentException($message)
193
    {
194 12
        if (strpos($message, 'Argument Exception') === 0) {
195 6
            if (strpos($message, 'The incoming token has expired.')) {
196 3
                throw new TokenExpiredException($message);
197
            }
198
199 3
            throw new ArgumentException($message);
200
        }
201 6
    }
202
203
    /**
204
     * @param $text
205
     * @param $to
206
     * @param $from
207
     * @return null|string
208
     */
209 15
    public function translate($text, $to, $from = null)
210
    {
211 15
        return $this->execute(new Translate($text, $to, $from));
212
    }
213
214
    /**
215
     * @param $text
216
     * @return null|Language
217
     * @throws TranslatorException
218
     */
219 3
    public function detect($text)
220
    {
221 3
        return $this->execute(new Detect($text));
222
    }
223
224
    /**
225
     * @param $text
226
     * @param $language
227
     * @param string $format
228
     * @param string $options
229
     * @return mixed
230
     * @throws TranslatorException
231
     */
232 3
    public function speak($text, $language, $format = Speak::FORMAT_MP3, $options = Speak::OPTION_MAX_QUALITY)
233
    {
234 3
        return $this->execute(new Speak($text, $language, $format, $options));
235
    }
236
237
    /**
238
     * @return Language[]
239
     * @throws TranslatorException
240
     */
241 3
    public function getLanguagesForSpeak()
242
    {
243 3
        return $this->execute(new GetLanguagesForSpeak());
244
    }
245
246
    /**
247
     * @param $languageCodes
248
     * @param string $locale
249
     * @return mixed
250
     * @throws TranslatorException
251
     */
252 6
    public function getLanguageNames($languageCodes, $locale = Language::ENGLISH)
253
    {
254 6
        return $this->execute(new GetLanguageNames($locale, $languageCodes));
255
    }
256
257
    /**
258
     * @return Language[]
259
     * @throws TranslatorException
260
     */
261 3
    public function getLanguagesForTranslate()
262
    {
263 3
        return $this->execute(new GetLanguagesForTranslate());
264
    }
265
}