Completed
Push — master ( c011fc...384d9c )
by Milan
01:49
created

src/Request/Queue.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace h4kuna\Fio\Request;
4
5
use GuzzleHttp,
6
	h4kuna\Fio,
7
	h4kuna\Fio\Response\Pay,
8
	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
	public function __construct($tempDir)
30
	{
31
		$this->tempDir = $tempDir;
32
	}
33
34
	/**
35
	 * @param int $limitLoop
36
	 */
37
	public function setLimitLoop($limitLoop)
38
	{
39
		$this->limitLoop = (int) $limitLoop;
40
	}
41
42
	/**
43
	 * @param array|\Iterator $downloadOptions
44
	 */
45
	public function setDownloadOptions($downloadOptions)
46
	{
47
		foreach ($downloadOptions as $define => $value) {
48
			if (is_string($define) && defined($define)) {
49
				$define = constant($define);
50
			}
51
			$this->downloadOptions[$define] = $value;
52
		}
53
	}
54
55
	public function setSleep($sleep)
56
	{
57
		$this->sleep = (bool) $sleep;
58
	}
59
60
	/**
61
	 * @param $token
62
	 * @param string $url
63
	 * @return mixed|string
64
	 * @throws Fio\QueueLimitException
65
	 * @throws Fio\ServiceUnavailableException
66
	 */
67
	public function download($token, $url)
68
	{
69
		$response = $this->request($token, function (GuzzleHttp\Client $client) use ($url) {
70
			return $client->request('GET', $url, $this->downloadOptions);
71
		});
72
		self::detectDownloadResponse($response);
73
		return $response->getBody();
74
	}
75
76
	/**
77
	 * @return Pay\IResponse
78
	 * @throws Fio\QueueLimitException
79
	 */
80
	public function upload($url, $token, array $post, $filename)
81
	{
82
		$newPost = [];
83
		foreach ($post as $name => $value) {
84
			$newPost[] = ['name' => $name, 'contents' => $value];
85
		}
86
		$newPost[] = ['name' => 'file', 'contents' => fopen($filename, 'r')];
87
88
		$response = $this->request($token, function (GuzzleHttp\Client $client) use ($url, $newPost) {
89
			return $client->request('POST', $url, [GuzzleHttp\RequestOptions::MULTIPART => $newPost]);
90
		});
91
		return self::createXmlResponse($response->getBody());
92
	}
93
94
	/**
95
	 * @param $token
96
	 * @param $fallback
97
	 * @return ResponseInterface
98
	 * @throws Fio\QueueLimitException
99
	 * @throws Fio\ServiceUnavailableException
100
	 */
101
	private function request($token, $fallback)
102
	{
103
		$request = new GuzzleHttp\Client(['headers' => ['X-Powered-By' => 'h4kuna/fio']]);
104
		$tempFile = $this->loadFileName($token);
105
		$file = fopen(self::safeProtocol($tempFile), 'w');
106
		$i = 0;
107
		do {
108
			$next = false;
109
			++$i;
110
			try {
111
				$response = $fallback($request);
112
			} catch (GuzzleHttp\Exception\ClientException $e) {
0 ignored issues
show
The class GuzzleHttp\Exception\ClientException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
113
				if ($e->getCode() !== self::HEADER_CONFLICT || !$this->sleep) {
114
					fclose($file);
115
					throw $e;
116
				} elseif ($i >= $this->limitLoop) {
117
					fclose($file);
118
					throw new Fio\QueueLimitException('You have limit up requests to server ' . $this->limitLoop);
119
				}
120
				self::sleep($tempFile);
121
				$next = true;
122
			} catch (GuzzleHttp\Exception\ServerException $e) {
0 ignored issues
show
The class GuzzleHttp\Exception\ServerException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
123
				if($e->hasResponse()) {
124
					self::detectDownloadResponse($e->getResponse());
125
				}
126
				throw self::createServiceUnavailableException();
127
			} catch (GuzzleHttp\Exception\ConnectException $e) {
0 ignored issues
show
The class GuzzleHttp\Exception\ConnectException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
128
				throw self::createServiceUnavailableException();
129
			}
130
		} while ($next);
131
		fclose($file);
132
		touch($tempFile);
133
134
		return $response;
135
	}
136
137
	/**
138
	 * @param ResponseInterface $response
139
	 * @throws Fio\ServiceUnavailableException
140
	 */
141
	private static function detectDownloadResponse($response)
142
	{
143
		/* @var $contentTypeHeaders array */
144
		$contentTypeHeaders = $response->getHeader('Content-Type');
145
		$contentType = array_shift($contentTypeHeaders);
146
		if ($contentType === 'text/xml;charset=UTF-8') {
147
			$xmlResponse = self::createXmlResponse($response);
148
			if ($xmlResponse->getErrorCode() !== 0) {
149
				throw new Fio\ServiceUnavailableException($xmlResponse->getError(), $xmlResponse->getErrorCode());
150
			}
151
		}
152
	}
153
154
	private static function sleep($filename)
155
	{
156
		$criticalTime = time() - filemtime($filename);
157
		if ($criticalTime < self::WAIT_TIME) {
158
			sleep(self::WAIT_TIME - $criticalTime);
159
		}
160
	}
161
162
	/**
163
	 * @param string $token
164
	 * @return string
165
	 */
166
	private function loadFileName($token)
167
	{
168
		$key = substr($token, 10, -10);
169
		if (!isset(self::$tokens[$key])) {
170
			self::$tokens[$key] = $this->tempDir . DIRECTORY_SEPARATOR . md5($key);
171
		}
172
173
		return self::$tokens[$key];
174
	}
175
176
	private static function safeProtocol($filename)
177
	{
178
		return Utils\SafeStream::PROTOCOL . '://' . $filename;
179
	}
180
181
	/**
182
	 * @param ResponseInterface|GuzzleHttp\Psr7\Stream $response
183
	 * @return Pay\XMLResponse
184
	 */
185
	private static function createXmlResponse($response)
186
	{
187
		return new Pay\XMLResponse($response->getBody()->getContents());
188
	}
189
190
191
	private static function createServiceUnavailableException()
192
	{
193
		return new Fio\ServiceUnavailableException('Fio server does not response.');
194
	}
195
196
}
197