Completed
Push — master ( 0770f8...218e56 )
by Ryosuke
21:28
created

CURLPool::addOrEnqueue()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 20
ccs 0
cts 15
cp 0
rs 8.8571
cc 6
eloc 14
nc 7
nop 2
crap 42
1
<?php
2
3
namespace mpyw\Co\Internal;
4
use mpyw\Co\Co;
5
use mpyw\Co\Internal\CoOption;
6
use mpyw\Co\CURLException;
7
use React\Promise\Deferred;
8
9
class CURLPool
10
{
11
    /**
12
     * Options.
13
     * @var CoOption
14
     */
15
    private $options;
16
17
    /**
18
     * cURL multi handle.
19
     * @var resource
20
     */
21
    private $mh;
22
23
    /**
24
     * The number of dispatched cURL handle.
25
     * @var int
26
     */
27
    private $count = 0;
28
29
    /**
30
     * cURL handles those have not been dispatched.
31
     * @var array
32
     */
33
    private $queue = [];
34
35
    /**
36
     * React Deferreds.
37
     * @var Deferred
38
     */
39
    private $deferreds = [];
40
41
    /**
42
     * Constructor.
43
     * Initialize cURL multi handle.
44
     * @param CoOption $options
45
     */
46 1
    public function __construct(CoOption $options)
47 1
    {
48 1
        $this->options = $options;
49 1
        $this->mh = curl_multi_init();
50 1
        $flags = (int)$options['pipeline'] + (int)$options['multiplex'] * 2;
51 1
        curl_multi_setopt($this->mh, CURLMOPT_PIPELINING, $flags);
52 1
    }
53
54
    /**
55
     * Call curl_multi_add_handle() or push into queue.
56
     * @param resource $ch
57
     * @param Deferred $deferred
58
     */
59
    public function addOrEnqueue($ch, $deferred = null)
60
    {
61
        if ($this->count >= $this->options['concurrency']) {
62
            if (isset($this->queue[(string)$ch])) {
63
                throw new \InvalidArgumentException("The cURL handle is already enqueued: $ch");
64
            }
65
            $this->queue[(string)$ch] = $ch;
66
        } else {
67
            $errno = curl_multi_add_handle($this->mh, $ch);
68
            if ($errno !== CURLM_OK) {
69
                $msg = curl_multi_strerror($errno) . ": $ch";
70
                $class = $errno === 7 ? '\InvalidArgumentException' : '\RuntimeException';
71
                throw new $class($msg);
72
            }
73
            ++$this->count;
74
        }
75
        if ($deferred) {
76
            $this->deferreds[(string)$ch] = $deferred;
77
        }
78
    }
79
80
    /**
81
     * Run curl_multi_exec() loop.
82
     */
83 1
    public function wait()
84 1
    {
85 1
        curl_multi_exec($this->mh, $active); // Start requests.
86
        do {
87 1
            curl_multi_select($this->mh, $this->options['interval']); // Wait events.
88 1
            curl_multi_exec($this->mh, $active);
89 1
            foreach ($this->readEntries() as $entry) {
90
                $r = $entry['result'] === CURLE_OK
91
                    ? curl_multi_getcontent($entry['handle'])
92
                    : new CURLException(curl_error($entry['handle']), $entry['result'], $entry['handle']);
93
                if (isset($this->deferreds[(string)$entry['handle']])) {
94
                    $deferred = $this->deferreds[(string)$entry['handle']];
95
                    unset($this->deferreds[(string)$entry['handle']]);
96
                    $r instanceof CURLException ? $deferred->reject($r) : $deferred->resolve($r);
97
                }
98
            }
99 1
        } while ($this->count > 0 || $this->queue);
100
        // All request must be done when reached here.
101 1
        if ($active) {
102
            throw new \LogicException('Unreachable statement.');
103
        }
104 1
    }
105
106
    /**
107
     * Read completed cURL handles.
108
     * @return array
109
     */
110 1
    private function readEntries()
111 1
    {
112 1
        $entries = [];
113 1
        while ($entry = curl_multi_info_read($this->mh)) {
114
            $entries[] = $entry;
115
        }
116 1
        foreach ($entries as $entry) {
117
            curl_multi_remove_handle($this->mh, $entry['handle']);
118
            --$this->count;
119
            if ($this->queue) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $this->queue of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
120
                $ch = array_shift($this->queue);
121
                $this->addOrEnqueue($ch);
122
            }
123
        }
124 1
        return $entries;
125
    }
126
}
127