Passed
Push — master ( 1eabfd...85f004 )
by Clinton
01:31
created

Guzzler::tick()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 2
nop 0
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace cvweiss;
4
5
class Guzzler
6
{
7
	private $curl;
8
	private $handler;
9
	private $client;
10
	private $concurrent = 0;
11
	private $maxConcurrent;
12
	private $usleep;
13
	private $lastHeaders = [];
14
	private $etagTTL = 86400;
15
16
	public function __construct($maxConcurrent = 10, $usleep = 100000, $userAgent = 'cvweiss/guzzler/', $curlOptions = [])
17
	{
18
		$curlOptions = $curlOptions == [] ? [CURLOPT_FRESH_CONNECT => false] : $curlOptions;
19
20
		$this->curl = new \GuzzleHttp\Handler\CurlMultiHandler();
21
		$this->handler = \GuzzleHttp\HandlerStack::create($this->curl);
22
		$this->client = new \GuzzleHttp\Client(['curl' => $curlOptions, 'connect_timeout' => 10, 'timeout' => 10, 'handler' => $this->handler, 'headers' => ['User-Agent' => $userAgent]]);
23
		$this->maxConcurrent = max($maxConcurrent, 1);
24
		$this->usleep = max(0, min(1000000, (int) $usleep));
25
	}
26
27
	public function tick()
28
	{
29
		$ms = microtime();
30
		do {
31
			$this->curl->tick();
32
			if ($this->concurrent >= $this->maxConcurrent) usleep(max(1, min(1000000, $this->usleep)));
33
		} while ($this->concurrent >= $this->maxConcurrent);
34
		return max(0, microtime() - $ms);
35
	}
36
37
	public function finish()
38
	{
39
		$ms = microtime();
40
		$this->curl->execute();
41
		return max(0, microtime() - $ms);
42
	}
43
44
	public function inc()
45
	{
46
		$this->concurrent++;
47
	}
48
49
	public function dec()
50
	{
51
		$this->concurrent--;
52
	}
53
54
	public function call($uri, $fulfilled, $rejected, $params = [], $setup = [], $callType = 'GET', $body = null)
55
	{
56
		$this->verifyCallable($fulfilled);
57
		$this->verifyCallable($rejected);
58
59
		while ($this->concurrent >= $this->maxConcurrent) $this->tick();
60
61
		$params['uri'] = $uri;
62
		$params['fulfilled'] = $fulfilled;
63
		$params['rejected'] = $rejected;
64
		$params['setup'] = $setup;
65
		$params['callType'] = $callType;
66
		$params['body'] = $body;
67
68
		$redis = $this->applyEtag($setup, $params);
69
70
		$guzzler = $this;
71
		$request = new \GuzzleHttp\Psr7\Request($callType, $uri, $setup, $body);
72
		$this->client->sendAsync($request)->then(
73
				function($response) use (&$guzzler, $fulfilled, &$params, $redis) {
74
				$guzzler->dec();
75
				$content = (string) $response->getBody();
76
				$this->lastHeaders = array_change_key_case($response->getHeaders());
77
				$this->applyEtagPost($this->lastHeaders, $params['uri'], $redis);
78
				$fulfilled($guzzler, $params, $content);
79
				},
80
				function($connectionException) use (&$guzzler, &$rejected, &$params) {
81
				$guzzler->dec();
82
				$response = $connectionException->getResponse();
83
				$this->lastHeaders = $response == null ? [] : array_change_key_case($response->getHeaders());
84
				$params['content'] = method_exists($response, "getBody") ? (string) $response->getBody() : "";
85
				$rejected($guzzler, $params, $connectionException);
86
				});
87
		$this->inc();
88
	}
89
90
	protected function applyEtag(&$setup, $params)
91
	{
92
		$redis = isset($setup['etag']) ? $setup['etag'] : null;
93
		if ($redis !== null && $params['callType'] == 'GET') {
94
			$etag = $redis->get("guzzler:etags:" . $params['uri']);
95
			if ($etag != "") $setup['If-None-Match'] = $etag;
96
		}
97
		unset($setup['etag']);
98
		return $redis;
99
	}
100
101
	protected function applyEtagPost($headers, $uri, $redis)
102
	{
103
		if (isset($headers['etag']) && $redis !== null) {
104
			$redis->setex("guzzler:etags:$uri", $this->etagTTL, $headers['etag'][0]);
105
		}
106
	}
107
108
	public function verifyCallable($callable)
109
	{
110
		if (!is_callable($callable)) {
111
			throw new \InvalidArgumentException(print_r($callable, true) . " is not a callable function");
112
		}
113
	}
114
115
	public function getLastHeaders()
116
	{
117
		return $this->lastHeaders;
118
	}
119
120
	public function setEtagTTL($ttl)
121
	{
122
		$this->etagTTL = $ttl;
123
	}
124
}
125