• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ringcentral / ringcentral-php / 10243692747

05 Aug 2024 06:22AM UTC coverage: 77.389% (-13.4%) from 90.761%
10243692747

push

github

web-flow
Merge pull request #137 from ringcentral/vendorPatch

Remove PubNub SubscriptionTest

575 of 743 relevant lines covered (77.39%)

7.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Subscription/PubnubSubscription.php
1
<?php
2

3
namespace RingCentral\SDK\Subscription;
4

5
use Exception;
6
use PubNub\Callbacks\SubscribeCallback;
7
use PubNub\Enums\PNStatusCategory;
8
use PubNub\Exceptions\PubNubUnsubscribeException;
9
use PubNub\Models\Consumer\PubSub\PNMessageResult;
10
use PubNub\PNConfiguration;
11
use PubNub\PubNub;
12
use PubNub\PubNubCrypto;
13
use RingCentral\SDK\Core\Utils;
14
use RingCentral\SDK\Http\ApiResponse;
15
use RingCentral\SDK\Platform\Platform;
16
use RingCentral\SDK\Subscription\Events\ErrorEvent;
17
use RingCentral\SDK\Subscription\Events\NotificationEvent;
18
use RingCentral\SDK\Subscription\Events\SuccessEvent;
19
use RingCentral\SDK\Subscription\SubscriptionBase;
20

21
class PubnubCallback extends SubscribeCallback
22
{
23
    /** @var Subscription */
24
    protected $_subscription;
25

26
    /**
27
     * PubnubCallback constructor.
28
     *
29
     * @param Subscription $subscription
30
     */
31
    function __construct(PubnubSubscription $subscription)
32
    {
33
        $this->_subscription = $subscription;
×
34
    }
35

36
    /**
37
     * @param $pubnub
38
     * @param $status
39
     *
40
     * @throws PubNubUnsubscribeException
41
     * @throws Exception
42
     *
43
     * @return void
44
     */
45
    function status($pubnub, $status)
46
    {
47

48
        if (!$this->_subscription->keepPolling()) {
×
49
            $sub = $this->_subscription->subscription();
×
50
            $e = new PubNubUnsubscribeException();
×
51
            $e->setChannels($sub['deliveryMode']['address']);
×
52
            throw $e;
×
53
        }
54

55
        $cat = $status->getCategory();
×
56

57
        if ($cat === PNStatusCategory::PNUnexpectedDisconnectCategory ||
×
58
            $cat === PNStatusCategory::PNTimeoutCategory
×
59
        ) {
60
            $this->_subscription->pubnubTimeoutHandler();
×
61
        }
62

63
    }
64

65
    /**
66
     * @param PubNub $pubnub
67
     * @param PNMessageResult $message
68
     *
69
     * @throws Exception
70
     *
71
     * @return bool
72
     */
73
    function message($pubnub, $message)
74
    {
75
        return $this->_subscription->notify($message);
×
76
    }
77

78
    function presence($pubnub, $presence)
79
    {
80
    }
×
81
}
82

83
class PubnubSubscription extends SubscriptionBase
84
{
85
    const RENEW_HANDICAP = 120; // 2 minutes
86
    const SUBSCRIBE_TIMEOUT = 60; // 1 minute
87

88
    /** @var Platform */
89
    protected $_platform;
90

91
    /** @var Pubnub */
92
    protected $_pubnub;
93

94
    protected $_keepPolling = false;
95

96
    protected $_skipSubscribe = false;
97

98
    function __construct(Platform $platform)
99
    {
100
        $this->_platform = $platform;
×
101
    }
102

103
    /**
104
     * @return Pubnub
105
     */
106
    function pubnub()
107
    {
108
        return $this->_pubnub;
×
109
    }
110

111
    /**
112
     * @param array $options
113
     *
114
     * @throws Exception
115
     *
116
     * @return ApiResponse|$this
117
     */
118
    function register(array $options = [])
119
    {
120
        if ($this->alive()) {
×
121
            return $this->renew($options);
×
122
        } else {
123
            return $this->subscribe($options);
×
124
        }
125
    }
126

127
    /**
128
     * @param bool $flag
129
     *
130
     * @return void
131
     */
132
    function setKeepPolling($flag = false)
133
    {
134
        $this->_keepPolling = !empty($flag);
×
135
    }
136

137
    /**
138
     * @return bool
139
     */
140
    function keepPolling()
141
    {
142
        return $this->_keepPolling;
×
143
    }
144

145
    /**
146
     * @param bool $flag
147
     *
148
     * @return void
149
     */
150
    function setSkipSubscribe($flag = false)
151
    {
152
        $this->_skipSubscribe = !empty($flag);
×
153
    }
154

155
    /**
156
     * @return bool
157
     */
158
    function skipSubscribe()
159
    {
160
        return $this->_skipSubscribe;
×
161
    }
162

163
    /**
164
     * @param array $options
165
     *
166
     * @throws Exception
167
     *
168
     * @return ApiResponse
169
     */
170
    function subscribe(array $options = [])
171
    {
172

173
        if (!empty($options['events'])) {
×
174
            $this->setEvents($options['events']);
×
175
        }
176

177
        try {
178

179
            $response = $this->_platform->post('/restapi/v1.0/subscription', [
×
180
                'eventFilters' => $this->getFullEventFilters(),
×
181
                'deliveryMode' => [
×
182
                    'transportType' => 'PubNub'
×
183
                ]
×
184
            ]);
×
185

186
            $this->setSubscription($response->jsonArray());
×
187
            $this->subscribeAtPubnub();
×
188

189
            //TODO Subscription renewal when everything will become async
190

191
            $this->dispatch(new SuccessEvent($response), self::EVENT_SUBSCRIBE_SUCCESS);
×
192

193
            return $response;
×
194

195
        } catch (Exception $e) {
×
196

197
            $this->reset();
×
198
            $this->dispatch(new ErrorEvent($e), self::EVENT_SUBSCRIBE_ERROR);
×
199
            throw $e;
×
200

201
        }
202

203
    }
204

205
    /**
206
     * @param array $options
207
     *
208
     * @throws Exception
209
     *
210
     * @return $this
211
     */
212
    function renew(array $options = [])
213
    {
214

215
        if (!empty($options['events'])) {
×
216
            $this->setEvents($options['events']);
×
217
        }
218

219
        if (!$this->subscribed()) {
×
220
            throw new Exception('No subscription');
×
221
        }
222

223
        try {
224

225
            $response = $this->_platform->put('/restapi/v1.0/subscription/' . $this->_subscription['id'], [
×
226
                'eventFilters' => $this->getFullEventFilters()
×
227
            ]);
×
228

229
            $this->setSubscription($response->jsonArray());
×
230

231
            $this->dispatch(new SuccessEvent($response), self::EVENT_RENEW_SUCCESS);
×
232

233
            return $this;
×
234

235
        } catch (Exception $e) {
×
236

237
            $this->reset();
×
238
            $this->dispatch(new ErrorEvent($e), self::EVENT_RENEW_ERROR);
×
239
            throw $e;
×
240

241
        }
242

243
    }
244

245
    /**
246
     * @throws Exception
247
     *
248
     * @return ApiResponse
249
     */
250
    function remove()
251
    {
252

253
        if (!$this->subscribed()) {
×
254
            throw new Exception('No subscription');
×
255
        }
256

257
        try {
258

259
            $response = $this->_platform->delete('/restapi/v1.0/subscription/' . $this->_subscription['id']);
×
260

261
            $this->reset();
×
262

263
            $this->dispatch(new SuccessEvent($response), self::EVENT_REMOVE_SUCCESS);
×
264

265
            return $response;
×
266

267
        } catch (Exception $e) {
×
268

269
            $this->reset();
×
270
            $this->dispatch(new ErrorEvent($e), self::EVENT_REMOVE_ERROR);
×
271
            throw $e;
×
272

273
        }
274

275
    }
276

277
    /**
278
     * @return bool
279
     */
280
    function subscribed()
281
    {
282
        return (!empty($this->_subscription) &&
×
283
                !empty($this->_subscription['deliveryMode']) &&
×
284
                !empty($this->_subscription['deliveryMode']['subscriberKey']) &&
×
285
                !empty($this->_subscription['deliveryMode']['address']));
×
286
    }
287

288
    /**
289
     * @return bool
290
     */
291
    function alive()
292
    {
293
        return $this->subscribed() && (time() < $this->expirationTime());
×
294
    }
295

296
    /**
297
     * @return int
298
     */
299
    function expirationTime()
300
    {
301
        return strtotime($this->_subscription['expirationTime']) - self::RENEW_HANDICAP;
×
302
    }
303

304
    function reset()
305
    {
306

307
        if ($this->_pubnub && $this->alive()) {
×
308
            //$this->_pubnub->unsubscribe($this->subscription['deliveryMode']['address']);
309
            $this->_pubnub = null;
×
310
        }
311

312
        $this->_subscription = null;
×
313

314
    }
315

316
    /**
317
     * @throws Exception
318
     *
319
     * @return $this
320
     */
321
    protected function subscribeAtPubnub()
322
    {
323

324
        if (!$this->alive()) {
×
325
            throw new Exception('Subscription is not alive');
×
326
        }
327

328
        $pnconf = new PNConfiguration();
×
329

330
        $pnconf->setUuid($this->_platform->auth()->data()['owner_id']);
×
331
        $pnconf->setSubscribeKey($this->_subscription['deliveryMode']['subscriberKey']);
×
332
        $pnconf->setPublishKey('convince-pubnub-its-okay');
×
333
        $pnconf->setSubscribeTimeout(self::SUBSCRIBE_TIMEOUT);
×
334

335
        $subscribeCallback = new PubnubCallback($this);
×
336

337
        $this->_pubnub = new PubNub($pnconf);
×
338
        $this->_pubnub->addListener($subscribeCallback);
×
339

340
        if (!$this->_skipSubscribe) {
×
341
            $this->_pubnub->subscribe()
×
342
                          ->channels($this->_subscription['deliveryMode']['address'])
×
343
                          ->execute();
×
344
        }
345

346
        return $this;
×
347

348
    }
349

350
    /**
351
     * Attention, this function is NOT PUBLIC!!! The only reason it's public is due to PHP 5.3 limitations
352
     * @protected
353
     *
354
     * @throws Exception
355
     */
356
    public function pubnubTimeoutHandler()
357
    {
358

359
        $this->dispatch(self::EVENT_TIMEOUT);
×
360

361
        if ($this->subscribed() && !$this->alive()) {
×
362
            $this->renew();
×
363
        }
364

365
    }
366

367
    /**
368
     * Attention, this function is NOT PUBLIC!!! The only reason it's public is due to PHP 5.3 limitations
369
     * @protected
370
     * @param PNMessageResult $pubnubMessage
371
     *
372
     * @throws Exception
373
     *
374
     * @return bool
375
     */
376
    public function notify($pubnubMessage)
377
    {
378
        $message = $pubnubMessage->getMessage();
×
379
        $message = $this->decrypt($message);
×
380
        //print 'Message received: ' . $message . PHP_EOL;
381
        $this->dispatch(new NotificationEvent($message), self::EVENT_NOTIFICATION);
×
382
        return $this->_keepPolling;
×
383
    }
384

385
    /**
386
     * @param $message
387
     *
388
     * @throws Exception
389
     *
390
     * @return bool|mixed|string
391
     */
392
    protected function decrypt($message)
393
    {
394

395
        if (!$this->subscribed()) {
×
396
            throw new Exception('No subscription');
×
397
        }
398

399
        if ($this->_subscription['deliveryMode']['encryption'] && $this->_subscription['deliveryMode']['encryptionKey']) {
×
400

401
            $aes = new PubNubCrypto($this->_subscription['deliveryMode']['encryptionKey'], false);
×
402

403
            $message = $aes->unPadPKCS7(
×
404
                openssl_decrypt(
×
405
                    base64_decode($message),
×
406
                    'AES-128-ECB',
×
407
                    base64_decode($this->_subscription['deliveryMode']['encryptionKey']),
×
408
                    OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
×
409
                ),
×
410
                128
×
411
            );
×
412

413
            $message = Utils::json_parse($message, true); // PUBNUB itself always decode as array
×
414

415
        }
416

417
        return $message;
×
418

419
    }
420
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc