Connection   A
last analyzed

Complexity

Total Complexity 39

Size/Duplication

Total Lines 285
Duplicated Lines 3.51 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

Changes 0
Metric Value
wmc 39
lcom 1
cbo 17
dl 10
loc 285
rs 9.28
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A sendSimple() 0 4 1
A sendE2E() 0 4 1
A uploadFile() 0 4 1
A downloadFile() 0 4 1
A keyLookupByPhoneNumber() 0 4 1
A keyLookupByEmail() 0 4 1
A keyCapability() 0 3 1
A credits() 0 3 1
A fetchPublicKey() 0 23 5
C createDefaultOptions() 10 51 12
A processRequestParams() 0 10 2
A get() 0 9 1
A post() 0 13 1
A postMultiPart() 0 15 1
B call() 0 28 8

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * @author Threema GmbH
4
 * @copyright Copyright (c) 2015-2016 Threema GmbH
5
 */
6
7
8
namespace Threema\MsgApi;
9
10
use Threema\Core\Exception;
11
use Threema\Core\Url;
12
use Threema\MsgApi\Commands\Capability;
13
use Threema\MsgApi\Commands\CommandInterface;
14
use Threema\MsgApi\Commands\Credits;
15
use Threema\MsgApi\Commands\DownloadFile;
16
use Threema\MsgApi\Commands\FetchPublicKey;
17
use Threema\MsgApi\Commands\LookupEmail;
18
use Threema\MsgApi\Commands\LookupPhone;
19
use Threema\MsgApi\Commands\MultiPartCommandInterface;
20
use Threema\MsgApi\Commands\Results\CapabilityResult;
21
use Threema\MsgApi\Commands\Results\DownloadFileResult;
22
use Threema\MsgApi\Commands\Results\FetchPublicKeyResult;
23
use Threema\MsgApi\Commands\Results\LookupIdResult;
24
use Threema\MsgApi\Commands\Results\Result;
25
use Threema\MsgApi\Commands\Results\SendSimpleResult;
26
use Threema\MsgApi\Commands\Results\SendE2EResult;
27
use Threema\MsgApi\Commands\Results\UploadFileResult;
28
use Threema\MsgApi\Commands\SendSimple;
29
use Threema\MsgApi\Commands\SendE2E;
30
use Threema\MsgApi\Commands\UploadFile;
31
use Threema\MsgApi\Constants;
32
33
/**
34
 * Class Connection
35
 * @package Threema\MsgApi
36
 */
37
class Connection
38
{
39
	/**
40
	 * @var ConnectionSettings
41
	 */
42
	private $setting;
43
44
	/**
45
	 * @var PublicKeyStore
46
	 */
47
	private $publicKeyStore;
48
49
	/**
50
	 * @param ConnectionSettings $setting
51
	 * @param PublicKeyStore $publicKeyStore stores the public keys locally to save network traffic
52
	 */
53
	public function __construct(ConnectionSettings $setting, PublicKeyStore $publicKeyStore = null) {
54
		$this->setting = $setting;
55
		$this->publicKeyStore = $publicKeyStore;
56
	}
57
58
	/**
59
	 * @param Receiver $receiver
60
	 * @param $text
61
	 * @return SendSimpleResult
62
	 */
63
	public function sendSimple(Receiver $receiver, $text) {
64
		$command = new SendSimple($receiver, $text);
65
		return $this->post($command);
66
	}
67
68
	/**
69
	 * @param string $threemaId
70
	 * @param string $nonce
71
	 * @param string $box
72
	 * @return SendE2EResult
73
	 */
74
	public function sendE2E($threemaId, $nonce, $box) {
75
		$command = new SendE2E($threemaId, $nonce, $box);
76
		return $this->post($command);
77
	}
78
79
	/**
80
	 * @param $encryptedFileData (binary string)
81
	 * @return UploadFileResult
82
	 */
83
	public function uploadFile($encryptedFileData) {
84
		$command = new UploadFile($encryptedFileData);
85
		return $this->postMultiPart($command);
86
	}
87
88
89
	/**
90
	 * @param $blobId
91
	 * @param callable $progress
92
	 * @return DownloadFileResult
93
	 */
94
	public function downloadFile($blobId, \Closure $progress = null) {
95
		$command = new DownloadFile($blobId);
96
		return $this->get($command, $progress);
97
	}
98
99
	/**
100
	 * @param $phoneNumber
101
	 * @return LookupIdResult
102
	 */
103
	public function keyLookupByPhoneNumber($phoneNumber) {
104
		$command = new LookupPhone($phoneNumber);
105
		return $this->get($command);
106
	}
107
108
	/**
109
	 * @param string $email
110
	 * @return LookupIdResult
111
	 */
112
	public function keyLookupByEmail($email) {
113
		$command = new LookupEmail($email);
114
		return $this->get($command);
115
	}
116
117
	/**
118
	 * @param string $threemaId valid threema id (8 Chars)
119
	 * @return CapabilityResult
120
	 */
121
	public function keyCapability($threemaId) {
122
		return $this->get(new Capability($threemaId));
123
	}
124
125
126
	/**
127
	 * @return CreditsResult
128
	 */
129
	public function credits() {
130
		return $this->get(new Credits());
131
	}
132
133
	/**
134
	 * @param $threemaId
135
	 * @return FetchPublicKeyResult
136
	 */
137
	public function fetchPublicKey($threemaId) {
138
		$publicKey = null;
139
140
		if (null !== $this->publicKeyStore) {
141
			$publicKey = $this->publicKeyStore->getPublicKey($threemaId);
142
		}
143
144
		if (null === $publicKey) {
145
			$command = new FetchPublicKey($threemaId);
146
			$result = $this->get($command);
147
			if (false === $result->isSuccess()) {
148
				return $result;
149
			}
150
			$publicKey = $result->getRawResponse();
151
152
			if (null !== $this->publicKeyStore) {
153
				$this->publicKeyStore->setPublicKey($threemaId, $publicKey);
154
			}
155
		}
156
157
		//create a key result
158
		return new FetchPublicKeyResult(200, $publicKey);
159
	}
160
161
	/**
162
	 * @param callable $progress
163
	 * @return array
164
	 */
165
	private function createDefaultOptions(\Closure $progress = null) {
166
		$options = array(
167
			CURLOPT_RETURNTRANSFER => true
168
		);
169
170
		//no progress
171
		if (null !== $progress) {
172
			$options[CURLOPT_NOPROGRESS] = false;
173
			$options[CURLOPT_PROGRESSFUNCTION] = $progress;
174
		}
175
176
		//tls settings
177
178
		if (true === $this->setting->getTlsOption(ConnectionSettings::tlsOptionForceHttps, false)) {
179
			//limit allowed protocols to HTTPS
180
			$options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTPS;
181
		}
182
		if ($tlsVersion = $this->setting->getTlsOption(ConnectionSettings::tlsOptionVersion)) {
183
			if (is_int($tlsVersion)) {
184
				//if number is given use it
185
				$options[CURLOPT_SSLVERSION] = $tlsVersion;
186
			} else {
187
				//interpret strings as TLS versions
188
				switch ($tlsVersion) {
189
					case '1.0':
190
						$options[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1_0;
191
						break;
192
					case '1.1':
193
						$options[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1_1;
194
						break;
195
					case '1.2':
196
						$options[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1_2;
197
						break;
198
					default:
199
						$options[CURLOPT_SSLVERSION] = CURL_SSLVERSION_DEFAULT;
200
						break;
201
				}
202
			}
203
		}
204 View Code Duplication
		if ($tlsCipher = $this->setting->getTlsOption(ConnectionSettings::tlsOptionCipher, null)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
205
			if(true === is_string($tlsCipher)) {
206
				$options[CURLOPT_SSL_CIPHER_LIST] = $tlsCipher;
207
			}
208
		}
209 View Code Duplication
		if ($pinnedKey = $this->setting->getTlsOption(ConnectionSettings::tlsOptionPinnedKey, Constants::DEFAULT_PINNED_KEY)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
210
			if(true === is_string($pinnedKey)) {
211
				$options[CURLOPT_PINNEDPUBLICKEY] = $pinnedKey;
212
			}
213
		}
214
		return $options;
215
	}
216
217
	/**
218
	 * @param array $params
219
	 * @return array
220
	 */
221
	private function processRequestParams(array $params) {
222
		if (null === $params) {
223
			$params = array();
224
		}
225
226
		$params['from'] = $this->setting->getThreemaId();
227
		$params['secret'] = $this->setting->getSecret();
228
229
		return $params;
230
	}
231
232
	/**
233
	 * @param CommandInterface $command
234
	 * @param callable $progress
235
	 * @return Result
236
	 */
237
	protected function get(CommandInterface $command, \Closure $progress = null) {
238
		$params = $this->processRequestParams($command->getParams());
239
		return $this->call($command->getPath(),
240
			$this->createDefaultOptions($progress),
241
			$params,
242
			function ($httpCode, $response) use ($command) {
243
				return $command->parseResult($httpCode, $response);
244
			});
245
	}
246
247
	/**
248
	 * @param CommandInterface $command
249
	 * @return Result
250
	 */
251
	protected function post(CommandInterface $command) {
252
		$options = $this->createDefaultOptions();
253
		$params = $this->processRequestParams($command->getParams());
254
255
		$options[CURLOPT_POST] = true;
256
		$options[CURLOPT_POSTFIELDS] = http_build_query($params);
257
		$options[CURLOPT_HTTPHEADER] = array(
258
			'Content-Type: application/x-www-form-urlencoded');
259
260
		return $this->call($command->getPath(), $options, null, function ($httpCode, $response) use ($command) {
261
			return $command->parseResult($httpCode, $response);
262
		});
263
	}
264
265
	/**
266
	 * @param MultiPartCommandInterface $command
267
	 * @return Result
268
	 */
269
	protected function postMultiPart(MultiPartCommandInterface $command) {
270
		$options = $this->createDefaultOptions();
271
		$params = $this->processRequestParams($command->getParams());
272
273
		$options[CURLOPT_POST] = true;
274
		$options[CURLOPT_HTTPHEADER] = array('Content-Type: multipart/form-data');
275
		$options[CURLOPT_SAFE_UPLOAD] = true;
276
		$options[CURLOPT_POSTFIELDS] = array(
277
			'blob' => $command->getData()
278
		);
279
280
		return $this->call($command->getPath(), $options, $params, function ($httpCode, $response) use ($command) {
281
			return $command->parseResult($httpCode, $response);
282
		});
283
	}
284
285
	/**
286
	 * @param string $path
287
	 * @param array $curlOptions
288
	 * @param array $parameters
289
	 * @param callable $result
290
	 * @return mixed
291
	 * @throws \Threema\Core\Exception
292
	 */
293
	private function call($path, array $curlOptions, array $parameters = null, \Closure $result = null) {
294
		$fullPath = new Url('', $this->setting->getHost());
295
		$fullPath->addPath($path);
296
297
		if (null !== $parameters && count($parameters)) {
298
			foreach ($parameters as $key => $value) {
299
				$fullPath->setValue($key, $value);
300
			}
301
		}
302
		$session = curl_init($fullPath->getFullPath());
303
		curl_setopt_array($session, $curlOptions);
304
305
		$response = curl_exec($session);
306
		if (false === $response) {
307
			throw new Exception($path . ' ' . curl_error($session));
308
		}
309
310
		$httpCode = curl_getinfo($session, CURLINFO_HTTP_CODE);
311
		if (null === $result && $httpCode != 200) {
312
			throw new Exception($httpCode);
313
		}
314
315
		if (null !== $result) {
316
			return $result->__invoke($httpCode, $response);
317
		} else {
318
			return $response;
319
		}
320
	}
321
}
322