Completed
Pull Request — master (#29)
by
unknown
01:23
created

Client::setNbConcurrentRequests()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
/*
4
 * This file is part of the Pushok package.
5
 *
6
 * (c) Arthur Edamov <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Pushok;
13
14
/**
15
 * Class Client
16
 * @package Pushok
17
 */
18
class Client
19
{
20
    /**
21
     * Array of notifications.
22
     *
23
     * @var Notification[]
24
     */
25
    private $notifications = [];
26
27
    /**
28
     * Authentication provider.
29
     *
30
     * @var AuthProviderInterface
31
     */
32
    private $authProvider;
33
34
    /**
35
     * Production or sandbox environment.
36
     *
37
     * @var bool
38
     */
39
    private $isProductionEnv;
40
41
    /**
42
     * Number of concurrent requests to multiplex in the same connection.
43
     *
44
     * @var int
45
     */
46
    private $nbConcurrentRequests = 20;
47
48
    /**
49
     * Number of maximum concurrent connections established to the APNS servers.
50
     *
51
     * @var int
52
     */
53
    private $maxConcurrentConnections = 1;
54
55
    /**
56
     * Flag to know if we should automatically close connections to the APNS servers or keep them alive.
57
     *
58
     * @var bool
59
     */
60
    private $autoCloseConnections = true;
61
62
    /**
63
     * Current curl_multi handle instance.
64
     *
65
     * @var Object
66
     */
67
    private $curlMultiHandle;
68
69
    /**
70
     * Client constructor.
71
     *
72
     * @param AuthProviderInterface $authProvider
73
     * @param bool $isProductionEnv
74
     */
75
    public function __construct(AuthProviderInterface $authProvider, bool $isProductionEnv = false)
76
    {
77
        $this->authProvider = $authProvider;
78
        $this->isProductionEnv = $isProductionEnv;
79
    }
80
81
    /**
82
     * Push notifications to APNs.
83
     *
84
     * @return ApnsResponseInterface[]
85
     */
86
    public function push(): array
87
    {
88
        if (!$this->curlMultiHandle) {
89
            $this->curlMultiHandle = curl_multi_init();
0 ignored issues
show
Documentation Bug introduced by
It seems like curl_multi_init() of type resource is incompatible with the declared type object of property $curlMultiHandle.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
90
91
            if (!defined('CURLPIPE_MULTIPLEX')) {
92
                define('CURLPIPE_MULTIPLEX', 2);
93
            }
94
95
            curl_multi_setopt($this->curlMultiHandle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
96
            curl_multi_setopt($this->curlMultiHandle, CURLMOPT_MAX_HOST_CONNECTIONS, $this->maxConcurrentConnections);
97
        }
98
99
        $mh = $this->curlMultiHandle;
100
101
        $i = 0;
102
        while (!empty($this->notifications) && $i++ < $this->nbConcurrentRequests) {
103
            $notification = array_pop($this->notifications);
104
            curl_multi_add_handle($mh, $this->prepareHandle($notification));
105
        }
106
107
        do {
108
            while (($execrun = curl_multi_exec($mh, $running)) == CURLM_CALL_MULTI_PERFORM);
109
110
            if ($execrun != CURLM_OK) {
111
                break;
112
            }
113
114
            while ($done = curl_multi_info_read($mh)) {
115
                $handle = $done['handle'];
116
117
                $result = curl_multi_getcontent($handle);
118
119
                // find out which token the response is about
120
                $token = curl_getinfo($handle, CURLINFO_PRIVATE);
121
122
                list($headers, $body) = explode("\r\n\r\n", $result, 2);
123
                $statusCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
124
                $responseCollection[] = new Response($statusCode, $headers, $body, $token);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$responseCollection was never initialized. Although not strictly required by PHP, it is generally a good practice to add $responseCollection = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
125
                curl_multi_remove_handle($mh, $handle);
126
                curl_close($handle);
127
128
                if (!empty($this->notifications)) {
129
                    $notification = array_pop($this->notifications);
130
                    curl_multi_add_handle($mh, $this->prepareHandle($notification));
131
                }
132
            }
133
        } while ($running);
134
135
        if ($this->autoCloseConnections) {
136
            curl_multi_close($mh);
137
            $this->curlMultiHandle = null;
138
        }
139
140
        return $responseCollection;
0 ignored issues
show
Bug introduced by
The variable $responseCollection does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
141
    }
142
143
    /**
144
     * Prepares a curl handle from a Notification object.
145
     *
146
     * @param Notification $notification
147
     */
148
    private function prepareHandle(Notification $notification)
149
    {
150
        $request = new Request($notification, $this->isProductionEnv);
151
        $ch = curl_init();
152
153
        $this->authProvider->authenticateClient($request);
154
155
        curl_setopt_array($ch, $request->getOptions());
156
        curl_setopt($ch, CURLOPT_HTTPHEADER, $request->getDecoratedHeaders());
157
158
        // store device token to identify response
159
        curl_setopt($ch, CURLOPT_PRIVATE, $notification->getDeviceToken());
160
161
        return $ch;
162
    }
163
164
    /**
165
     * Add notification in queue for sending.
166
     *
167
     * @param Notification $notification
168
     */
169
    public function addNotification(Notification $notification)
170
    {
171
        $this->notifications[] = $notification;
172
    }
173
174
    /**
175
     * Add several notifications in queue for sending.
176
     *
177
     * @param Notification[] $notifications
178
     */
179
    public function addNotifications(array $notifications)
180
    {
181
        foreach ($notifications as $notification) {
182
            if (in_array($notification, $this->notifications, true)) {
183
                continue;
184
            }
185
186
            $this->addNotification($notification);
187
        }
188
    }
189
190
    /**
191
     * Get already added notifications.
192
     *
193
     * @return Notification[]
194
     */
195
    public function getNotifications(): array
196
    {
197
        return $this->notifications;
198
    }
199
200
    /**
201
     * Close the current curl multi handle.
202
     */
203
    public function close()
204
    {
205
        if ($this->curlMultiHandle) {
206
            curl_multi_close($this->curlMultiHandle);
207
            $this->curlMultiHandle = null;
208
        }
209
    }
210
211
    /**
212
     * Set the number of concurrent requests sent through the multiplexed connections.
213
     *
214
     * @param int $nbConcurrentRequests
215
     */
216
    public function setNbConcurrentRequests($nbConcurrentRequests)
217
    {
218
        $this->nbConcurrentRequests = $nbConcurrentRequests;
219
    }
220
221
222
    /**
223
     * Set the number of maximum concurrent connections established to the APNS servers.
224
     *
225
     * @param int $nbConcurrentRequests
0 ignored issues
show
Bug introduced by
There is no parameter named $nbConcurrentRequests. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
226
     */
227
    public function setMaxConcurrentConnections($maxConcurrentConnections)
228
    {
229
        $this->maxConcurrentConnections = $maxConcurrentConnections;
230
    }
231
232
    /**
233
     * Set wether or not the client should automatically close the connections. Apple recommends keeping
234
     * connections open if you send more than a few notification per minutes.
235
     *
236
     * @param bool $nbConcurrentRequests
0 ignored issues
show
Bug introduced by
There is no parameter named $nbConcurrentRequests. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
237
     */
238
    public function setAutoCloseConnections($autoCloseConnections)
239
    {
240
        $this->autoCloseConnections = $autoCloseConnections;
241
    }
242
}
243