Passed
Push — master ( 517e0c...e2aca6 )
by Clinton
01:42
created

Guzzler::applyEtag()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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