Completed
Pull Request — master (#80)
by Hiraku
02:20
created

CurlMulti::setupShareHandler()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
ccs 0
cts 0
cp 0
rs 9.4285
cc 3
eloc 6
nc 3
nop 0
crap 12
1
<?php
2
/*
3
 * hirak/prestissimo
4
 * @author Hiraku NAKANO
5
 * @license MIT https://github.com/hirak/prestissimo
6
 */
7
namespace Hirak\Prestissimo;
8
9
class CurlMulti
10
{
11
    /** @var resource curl_multi */
12
    private $mh;
13
14
    /** @var resource[] curl */
15
    private $unused = array();
16
17
    /** @var resource[] curl */
18
    private $using = array();
19
20
    /** @var array {src: HttpGetRequest, dest: OutputFile}*/
21
    private $targets;
22
23
    /** @var array {src: HttpGetRequest, dest: OutputFile}*/
24
    private $runningTargets;
25
26
    /**
27
     * @param int $maxConnections
28
     */
29 3
    public function __construct($maxConnections)
30
    {
31 3
        $this->mh = curl_multi_init();
32
33 3
        for ($i = 0; $i < $maxConnections; ++$i) {
34 3
            $this->unused[] = curl_init();
35 3
        }
36 3
    }
37
38 3
    public function __destruct()
39
    {
40 3
        foreach ($this->using as $ch) {
41
            curl_multi_remove_handle($this->mh, $ch);
42
            curl_close($ch);
43 3
        }
44
45 3
        foreach ($this->unused as $ch) {
46 3
            curl_close($ch);
47 3
        }
48
49 3
        curl_multi_close($this->mh);
50 3
    }
51
52
    /**
53
     * @codeCoverageIgnore
54
     */
55
    public function setupShareHandler()
56
    {
57
        if (function_exists('curl_share_init')) {
58
            $sh = curl_share_init();
59
            curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
60
61
            foreach ($this->unused as $ch) {
62
                curl_setopt($ch, CURLOPT_SHARE, $sh);
63
            }
64
        }
65
    }
66
67
    /**
68
     * @param array $targets {src: HttpGetRequest, dest: OutputFile}
69
     */
70 3
    public function setTargets(array $targets)
71
    {
72 3
        $this->targets = $targets;
73 3
    }
74
75 3
    public function setupEventLoop()
76
    {
77 3
        while (count($this->unused) > 0 && count($this->targets) > 0) {
78 2
            $target = array_pop($this->targets);
79 2
            $ch = array_pop($this->unused);
80 2
            $index = (int)$ch;
81
82 2
            $this->using[$index] = $ch;
83 2
            $this->runningTargets[$index] = $target;
84
85 2
            $opts = $target['src']->getCurlOpts();
86 2
            unset($opts[CURLOPT_ENCODING], $opts[CURLOPT_USERPWD]);
87 2
            curl_setopt_array($ch, $opts);
88 2
            curl_setopt($ch, CURLOPT_FILE, $target['dest']->getPointer());
89 2
            curl_multi_add_handle($this->mh, $ch);
90 2
        }
91 3
    }
92
93 3
    public function wait()
94
    {
95 3
        $expectRunning = count($this->using);
96 3
        $running = 0;
97 3
        $retryCnt = 0;
98
99
        do {
100
            do {
101 3
                $stat = curl_multi_exec($this->mh, $running);
102 3
            } while ($stat === CURLM_CALL_MULTI_PERFORM);
103 3
            if (-1 === curl_multi_select($this->mh)) {
104
                // @codeCoverageIgnoreStart
105
                if ($retryCnt++ > 100) {
106
                    throw new \RuntimeException('curl_multi_select failure');
107
                }
108
                // @codeCoverageIgnoreEnd
109 3
                usleep(100000);
110 3
            }
111 3
        } while ($running > 0 && $running >= $expectRunning);
112 3
    }
113
114 3
    public function getFinishedResults()
115
    {
116 3
        $results = array();
117 3
        $successCnt = $failureCnt = 0;
118
        do {
119 3
            if ($raised = curl_multi_info_read($this->mh, $remains)) {
120 2
                $ch = $raised['handle'];
121 2
                $errno = curl_errno($ch);
122 2
                $info = curl_getinfo($ch);
123 2
                curl_setopt($ch, CURLOPT_FILE, STDOUT);
124 2
                $index = (int)$ch;
125 2
                $target = $this->runningTargets[$index];
126 2
                if (CURLE_OK === $errno && 200 === $info['http_code']) {
127 1
                    ++$successCnt;
128 1
                    $target['dest']->setSuccess();
129 1
                    $results[] = $info['url'];
130 1
                } else {
131 1
                    ++$failureCnt;
132
                }
133 2
                unset($this->using[$index], $this->runningTargets[$index], $target);
134 2
                curl_multi_remove_handle($this->mh, $ch);
135 2
                $this->unused[] = $ch;
136 2
            }
137 3
        } while ($remains > 0);
138
139 3
        return compact('successCnt', 'failureCnt', 'results');
140
    }
141
142 3
    public function remain()
143
    {
144 3
        return count($this->runningTargets) > 0;
145
    }
146
}
147