Completed
Push — master ( 589fec...539028 )
by Milan
07:58
created

Queue::createFileResource()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php declare(strict_types=1);
2
3
namespace h4kuna\Fio\Request;
4
5
use GuzzleHttp;
6
use h4kuna\Fio\Exceptions;
7
use h4kuna\Fio\Response\Pay;
8
use Nette\Utils;
9
use Psr\Http\Message\ResponseInterface;
10
11
class Queue implements IQueue
12
{
13
14
	/** @var string[] */
15
	private static $tokens = [];
16
17
	/** @var int */
18
	private $limitLoop = 5;
19
20
	/** @var bool */
21
	private $sleep = true;
22
23
	/** @var array */
24
	private $downloadOptions = [];
25
26
	/** @var string */
27
	private $tempDir;
28
29
30
	public function __construct(string $tempDir)
31
	{
32
		$this->tempDir = $tempDir;
33
	}
34
35
36
	public function setLimitLoop(int $limitLoop): void
37
	{
38
		$this->limitLoop = $limitLoop;
39
	}
40
41
42
	public function setDownloadOptions(iterable $downloadOptions): void
43
	{
44
		foreach ($downloadOptions as $define => $value) {
45
			if (is_string($define) && defined($define)) {
46
				$define = constant($define);
47
			}
48
			$this->downloadOptions[$define] = $value;
49
		}
50
	}
51
52
53
	public function setSleep(bool $sleep): void
54
	{
55
		$this->sleep = $sleep;
56
	}
57
58
59
	/**
60
	 * @throws Exceptions\QueueLimit
61
	 * @throws Exceptions\ServiceUnavailable
62
	 */
63
	public function download(string $token, string $url): string
64
	{
65
		$response = $this->request($token, function (GuzzleHttp\ClientInterface $client) use ($url) {
66
			return $client->request('GET', $url, $this->downloadOptions);
67
		});
68
		$this->detectDownloadResponse($response);
69
		return (string) $response->getBody();
70
	}
71
72
73
	/**
74
	 * @throws Exceptions\QueueLimit
75
	 * @throws Exceptions\ServiceUnavailable
76
	 */
77
	public function upload(string $url, string $token, array $post, string $filename): Pay\IResponse
78
	{
79
		$newPost = [];
80
		foreach ($post as $name => $value) {
81
			$newPost[] = ['name' => $name, 'contents' => $value];
82
		}
83
		$newPost[] = ['name' => 'file', 'contents' => fopen($filename, 'r')];
84
85
		$response = $this->request($token, function (GuzzleHttp\ClientInterface $client) use ($url, $newPost) {
86
			return $client->request('POST', $url, [GuzzleHttp\RequestOptions::MULTIPART => $newPost]);
87
		});
88
		return $this->createXmlResponse($response);
89
	}
90
91
92
	/**
93
	 * @throws Exceptions\QueueLimit
94
	 * @throws Exceptions\ServiceUnavailable()
95
	 */
96
	private function request(string $token, callable $fallback): ResponseInterface
97
	{
98
		$client = $this->createClient();
99
		$tempFile = $this->loadFileName($token);
100
		$file = self::createFileResource($tempFile);
101
		$i = 0;
102
		do {
103
			$next = false;
0 ignored issues
show
Unused Code introduced by
$next is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
104
			++$i;
105
			try {
106
				$response = $fallback($client);
107
				fclose($file);
108
				touch($tempFile);
109
				return $response;
110
			} catch (GuzzleHttp\Exception\ClientException $e) {
111
				if ($e->getCode() !== self::HEADER_CONFLICT || !$this->sleep) {
112
					fclose($file);
113
					throw $e;
114
				} elseif ($i >= $this->limitLoop) {
115
					fclose($file);
116
					throw new Exceptions\QueueLimit('You have limit up requests to server ' . $this->limitLoop);
117
				}
118
				self::sleep($tempFile);
119
				$next = true;
120
			} catch (GuzzleHttp\Exception\BadResponseException $e) {
121
				throw new Exceptions\ServiceUnavailable($e->getMessage(), $e->getCode(), $e);
122
			}
123
		} while ($next);
124
	}
125
126
127
	private static function createFileResource(string $filePath)
128
	{
129
		$file = fopen(self::safeProtocol($filePath), 'w');
130
		if ($file === false) {
131
			throw new Exceptions\InvalidState('Open file is failed ' . $filePath);
132
		}
133
		return $file;
134
	}
135
136
137
	/**
138
	 * @throws Exceptions\ServiceUnavailable()
139
	 */
140
	private function detectDownloadResponse(ResponseInterface $response): void
141
	{
142
		/* @var $contentTypeHeaders array */
143
		$contentTypeHeaders = $response->getHeader('Content-Type');
144
		$contentType = array_shift($contentTypeHeaders);
145
		if ($contentType === 'text/xml;charset=UTF-8') {
146
			$xmlResponse = $this->createXmlResponse($response);
147
			if ($xmlResponse->code() !== 0) {
148
				throw new Exceptions\ServiceUnavailable($xmlResponse->status(), $xmlResponse->code());
149
			}
150
		}
151
	}
152
153
154
	private static function sleep(string $filename): void
155
	{
156
		$criticalTime = time() - filemtime($filename);
157
		if ($criticalTime < static::WAIT_TIME) {
158
			sleep(static::WAIT_TIME - $criticalTime);
159
		}
160
	}
161
162
163
	private function loadFileName(string $token): string
164
	{
165
		$key = substr($token, 10, -10);
166
		if (!isset(self::$tokens[$key])) {
167
			self::$tokens[$key] = $this->tempDir . DIRECTORY_SEPARATOR . md5($key);
168
		}
169
170
		return self::$tokens[$key];
171
	}
172
173
174
	private static function safeProtocol(string $filename): string
175
	{
176
		return Utils\SafeStream::PROTOCOL . '://' . $filename;
177
	}
178
179
180
	protected function createXmlResponse(ResponseInterface $response): Pay\IResponse
181
	{
182
		return new Pay\XMLResponse($response->getBody()->getContents());
183
	}
184
185
186
	protected function createClient(): GuzzleHttp\ClientInterface
187
	{
188
		return new GuzzleHttp\Client(['headers' => ['X-Powered-By' => 'h4kuna/fio']]);
189
	}
190
191
}
192