Completed
Push — master ( 21f44f...e6f104 )
by Ryosuke
02:48
created

CURLPool::readEntries()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 16
ccs 12
cts 12
cp 1
rs 9.2
cc 4
eloc 11
nc 6
nop 0
crap 4
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
     * cURL handles those have not been dispatched.
25
     * @var array
26
     */
27
    private $queue = [];
28
29
    /**
30
     * cURL handles those have been already dispatched.
31
     * @var array
32
     */
33
    private $added = [];
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 6
    public function __construct(CoOption $options)
47 6
    {
48 6
        $this->options = $options;
49 6
        $this->mh = curl_multi_init();
50 6
        $flags = (int)$options['pipeline'] + (int)$options['multiplex'] * 2;
51 6
        curl_multi_setopt($this->mh, CURLMOPT_PIPELINING, $flags);
52 6
    }
53
54
    /**
55
     * Call curl_multi_add_handle() or push into queue.
56
     * @param resource $ch
57
     * @param Deferred $deferred
58
     */
59 4
    public function addOrEnqueue($ch, $deferred = null)
60 4
    {
61 4
        if (isset($this->added[(string)$ch]) || isset($this->queue[(string)$ch])) {
62 3
            throw new \InvalidArgumentException("The cURL handle is already enqueued: $ch");
63
        }
64 4
        if (count($this->added) >= $this->options['concurrency']) {
65 2
            $this->queue[(string)$ch] = $ch;
66 2
            $deferred and $this->deferreds[(string)$ch] = $deferred;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
67 2
            return;
68
        }
69 4
        $errno = curl_multi_add_handle($this->mh, $ch);
70 4
        if ($errno !== CURLM_OK) {
71
            $msg = curl_multi_strerror($errno) . ": $ch";
72
            $deferred->reject(new \RuntimeException($msg));
0 ignored issues
show
Bug introduced by
It seems like $deferred is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
73
            return;
74
        }
75 4
        $this->added[(string)$ch] = $ch;
76 4
        $deferred and $this->deferreds[(string)$ch] = $deferred;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
77 4
    }
78
79
    /**
80
     * Run curl_multi_exec() loop.
81
     */
82 3
    public function wait()
83 3
    {
84 3
        curl_multi_exec($this->mh, $active); // Start requests.
85
        do {
86 3
            curl_multi_select($this->mh, $this->options['interval']); // Wait events.
87 3
            curl_multi_exec($this->mh, $active);
88 3
            foreach ($this->readEntries() as $entry) {
89 1
                $r = $entry['result'] === CURLE_OK
90 1
                    ? curl_multi_getcontent($entry['handle'])
91 1
                    : new CURLException(curl_error($entry['handle']), $entry['result'], $entry['handle']);
92 1
                if (isset($this->deferreds[(string)$entry['handle']])) {
93 1
                    $deferred = $this->deferreds[(string)$entry['handle']];
94 1
                    unset($this->deferreds[(string)$entry['handle']]);
95 1
                    $r instanceof CURLException ? $deferred->reject($r) : $deferred->resolve($r);
96
                }
97
            }
98 3
        } while ($this->added || $this->queue);
1 ignored issue
show
Bug Best Practice introduced by
The expression $this->added 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...
99
        // All request must be done when reached here.
100 3
        if ($active) {
101
            throw new \LogicException('Unreachable statement.');
102
        }
103 3
    }
104
105
    /**
106
     * Read completed cURL handles.
107
     * @return array
108
     */
109 3
    private function readEntries()
110 3
    {
111 3
        $entries = [];
112 3
        while ($entry = curl_multi_info_read($this->mh)) {
113 1
            $entries[] = $entry;
114
        }
115 3
        foreach ($entries as $entry) {
116 1
            curl_multi_remove_handle($this->mh, $entry['handle']);
117 1
            unset($this->added[(string)$entry['handle']]);
118 1
            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...
119 1
                $ch = array_shift($this->queue);
120 1
                $this->addOrEnqueue($ch);
121
            }
122
        }
123 3
        return $entries;
124
    }
125
}
126