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

ringcentral / ringcentral-php / 271

pending completion
271

push

travis-ci-com

web-flow
Merge pull request #115 from ringcentral/feat/accept

feat: remove default accept

1 of 1 new or added line in 1 file covered. (100.0%)

470 of 522 relevant lines covered (90.04%)

9.5 hits per line

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

81.2
/src/Subscription/Subscription.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 Symfony\Component\EventDispatcher\EventDispatcher;
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(Subscription $subscription)
32
    {
33
        $this->_subscription = $subscription;
9✔
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 Subscription extends EventDispatcher
84
{
85

86
    const EVENT_NOTIFICATION = 'notification';
87
    const EVENT_REMOVE_SUCCESS = 'removeSuccess';
88
    const EVENT_REMOVE_ERROR = 'removeError';
89
    const EVENT_RENEW_SUCCESS = 'renewSuccess';
90
    const EVENT_RENEW_ERROR = 'renewError';
91
    const EVENT_SUBSCRIBE_SUCCESS = 'subscribeSuccess';
92
    const EVENT_SUBSCRIBE_ERROR = 'subscribeError';
93
    const EVENT_TIMEOUT = 'timeout';
94

95
    const RENEW_HANDICAP = 120; // 2 minutes
96
    const SUBSCRIBE_TIMEOUT = 60; // 1 minute
97

98
    /** @var Platform */
99
    protected $_platform;
100

101
    /** @var string[] */
102
    protected $_eventFilters = [];
103

104
    /** @var array */
105
    protected $_subscription = [
106
        'eventFilters'   => [],
107
        'expirationTime' => '', // 2014-03-12T19:54:35.613Z
108
        'expiresIn'      => 0,
109
        'deliveryMode'   => [
110
            'transportType' => 'PubNub',
111
            'encryption'    => false,
112
            'address'       => '',
113
            'subscriberKey' => '',
114
            'secretKey'     => ''
115
        ],
116
        'id'             => '',
117
        'creationTime'   => '', // 2014-03-12T19:54:35.613Z
118
        'status'         => '', // Active
119
        'uri'            => ''
120
    ];
121

122
    /** @var Pubnub */
123
    protected $_pubnub;
124

125
    protected $_keepPolling = false;
126

127
    protected $_skipSubscribe = false;
128

129
    function __construct(Platform $platform)
130
    {
131
        $this->_platform = $platform;
13✔
132
    }
133

134
    /**
135
     * @return Pubnub
136
     */
137
    function pubnub()
138
    {
139
        return $this->_pubnub;
1✔
140
    }
141

142
    /**
143
     * @param array $options
144
     *
145
     * @throws Exception
146
     *
147
     * @return ApiResponse|$this
148
     */
149
    function register(array $options = [])
150
    {
151
        if ($this->alive()) {
7✔
152
            return $this->renew($options);
1✔
153
        } else {
154
            return $this->subscribe($options);
7✔
155
        }
156
    }
157

158
    /**
159
     * @param bool $flag
160
     *
161
     * @return void
162
     */
163
    function setKeepPolling($flag = false)
164
    {
165
        $this->_keepPolling = !empty($flag);
1✔
166
    }
167

168
    /**
169
     * @return bool
170
     */
171
    function keepPolling()
172
    {
173
        return $this->_keepPolling;
1✔
174
    }
175

176
    /**
177
     * @param bool $flag
178
     *
179
     * @return void
180
     */
181
    function setSkipSubscribe($flag = false)
182
    {
183
        $this->_skipSubscribe = !empty($flag);
13✔
184
    }
185

186
    /**
187
     * @return bool
188
     */
189
    function skipSubscribe()
190
    {
191
        return $this->_skipSubscribe;
×
192
    }
193

194
    /**
195
     * @param array $events
196
     *
197
     * @return $this
198
     */
199
    function addEvents(array $events)
200
    {
201
        $this->_eventFilters = array_merge($this->_eventFilters, $events);
2✔
202
        return $this;
2✔
203
    }
204

205
    /**
206
     * @param array $events
207
     *
208
     * @return $this
209
     */
210
    function setEvents(array $events)
211
    {
212
        $this->_eventFilters = $events;
9✔
213
        return $this;
9✔
214
    }
215

216
    /**
217
     * @param array $options
218
     *
219
     * @throws Exception
220
     *
221
     * @return ApiResponse
222
     */
223
    function subscribe(array $options = [])
224
    {
225

226
        if (!empty($options['events'])) {
11✔
227
            $this->setEvents($options['events']);
9✔
228
        }
229

230
        try {
231

232
            $response = $this->_platform->post('/restapi/v1.0/subscription', [
11✔
233
                'eventFilters' => $this->getFullEventFilters(),
11✔
234
                'deliveryMode' => [
11✔
235
                    'transportType' => 'PubNub'
11✔
236
                ]
11✔
237
            ]);
11✔
238

239
            $this->setSubscription($response->jsonArray());
9✔
240
            $this->subscribeAtPubnub();
9✔
241

242
            //TODO Subscription renewal when everything will become async
243

244
            $this->dispatch(new SuccessEvent($response), self::EVENT_SUBSCRIBE_SUCCESS);
9✔
245

246
            return $response;
9✔
247

248
        } catch (Exception $e) {
2✔
249

250
            $this->reset();
2✔
251
            $this->dispatch(new ErrorEvent($e), self::EVENT_SUBSCRIBE_ERROR);
2✔
252
            throw $e;
2✔
253

254
        }
255

256
    }
257

258
    /**
259
     * @param array $options
260
     *
261
     * @throws Exception
262
     *
263
     * @return $this
264
     */
265
    function renew(array $options = [])
266
    {
267

268
        if (!empty($options['events'])) {
3✔
269
            $this->setEvents($options['events']);
3✔
270
        }
271

272
        if (!$this->subscribed()) {
3✔
273
            throw new Exception('No subscription');
×
274
        }
275

276
        try {
277

278
            $response = $this->_platform->put('/restapi/v1.0/subscription/' . $this->_subscription['id'], [
3✔
279
                'eventFilters' => $this->getFullEventFilters()
3✔
280
            ]);
3✔
281

282
            $this->setSubscription($response->jsonArray());
2✔
283

284
            $this->dispatch(new SuccessEvent($response), self::EVENT_RENEW_SUCCESS);
2✔
285

286
            return $this;
2✔
287

288
        } catch (Exception $e) {
1✔
289

290
            $this->reset();
1✔
291
            $this->dispatch(new ErrorEvent($e), self::EVENT_RENEW_ERROR);
1✔
292
            throw $e;
1✔
293

294
        }
295

296
    }
297

298
    /**
299
     * @throws Exception
300
     *
301
     * @return ApiResponse
302
     */
303
    function remove()
304
    {
305

306
        if (!$this->subscribed()) {
2✔
307
            throw new Exception('No subscription');
×
308
        }
309

310
        try {
311

312
            $response = $this->_platform->delete('/restapi/v1.0/subscription/' . $this->_subscription['id']);
2✔
313

314
            $this->reset();
1✔
315

316
            $this->dispatch(new SuccessEvent($response), self::EVENT_REMOVE_SUCCESS);
1✔
317

318
            return $response;
1✔
319

320
        } catch (Exception $e) {
1✔
321

322
            $this->reset();
1✔
323
            $this->dispatch(new ErrorEvent($e), self::EVENT_REMOVE_ERROR);
1✔
324
            throw $e;
1✔
325

326
        }
327

328
    }
329

330
    /**
331
     * @return bool
332
     */
333
    function subscribed()
334
    {
335
        return (!empty($this->_subscription) &&
11✔
336
                !empty($this->_subscription['deliveryMode']) &&
11✔
337
                !empty($this->_subscription['deliveryMode']['subscriberKey']) &&
11✔
338
                !empty($this->_subscription['deliveryMode']['address']));
11✔
339
    }
340

341
    /**
342
     * @return bool
343
     */
344
    function alive()
345
    {
346
        return $this->subscribed() && (time() < $this->expirationTime());
11✔
347
    }
348

349
    /**
350
     * @return int
351
     */
352
    function expirationTime()
353
    {
354
        return strtotime($this->_subscription['expirationTime']) - self::RENEW_HANDICAP;
9✔
355
    }
356

357
    /**
358
     * @return array
359
     */
360
    function subscription()
361
    {
362
        return $this->_subscription;
3✔
363
    }
364

365
    /**
366
     * @param array $subscription
367
     *
368
     * @return $this
369
     */
370
    function setSubscription($subscription)
371
    {
372
        $this->_subscription = $subscription;
9✔
373
        return $this;
9✔
374
    }
375

376
    function reset()
377
    {
378

379
        if ($this->_pubnub && $this->alive()) {
5✔
380
            //$this->_pubnub->unsubscribe($this->subscription['deliveryMode']['address']);
381
            $this->_pubnub = null;
3✔
382
        }
383

384
        $this->_subscription = null;
5✔
385

386
    }
387

388
    /**
389
     * @throws Exception
390
     *
391
     * @return $this
392
     */
393
    protected function subscribeAtPubnub()
394
    {
395

396
        if (!$this->alive()) {
9✔
397
            throw new Exception('Subscription is not alive');
×
398
        }
399

400
        $pnconf = new PNConfiguration();
9✔
401

402
        $pnconf->setSubscribeKey($this->_subscription['deliveryMode']['subscriberKey']);
9✔
403
        $pnconf->setPublishKey('convince-pubnub-its-okay');
9✔
404
        $pnconf->setSubscribeTimeout(self::SUBSCRIBE_TIMEOUT);
9✔
405

406
        $subscribeCallback = new PubnubCallback($this);
9✔
407

408
        $this->_pubnub = new PubNub($pnconf);
9✔
409
        $this->_pubnub->addListener($subscribeCallback);
9✔
410

411
        if (!$this->_skipSubscribe) {
9✔
412
            $this->_pubnub->subscribe()
×
413
                          ->channels($this->_subscription['deliveryMode']['address'])
×
414
                          ->execute();
×
415
        }
416

417
        return $this;
9✔
418

419
    }
420

421
    /**
422
     * Attention, this function is NOT PUBLIC!!! The only reason it's public is due to PHP 5.3 limitations
423
     * @protected
424
     *
425
     * @throws Exception
426
     */
427
    public function pubnubTimeoutHandler()
428
    {
429

430
        $this->dispatch(self::EVENT_TIMEOUT);
×
431

432
        if ($this->subscribed() && !$this->alive()) {
×
433
            $this->renew();
×
434
        }
435

436
    }
437

438
    /**
439
     * Attention, this function is NOT PUBLIC!!! The only reason it's public is due to PHP 5.3 limitations
440
     * @protected
441
     * @param PNMessageResult $pubnubMessage
442
     *
443
     * @throws Exception
444
     *
445
     * @return bool
446
     */
447
    public function notify($pubnubMessage)
448
    {
449
        $message = $pubnubMessage->getMessage();
2✔
450
        $message = $this->decrypt($message);
2✔
451
        //print 'Message received: ' . $message . PHP_EOL;
452
        $this->dispatch(new NotificationEvent($message), self::EVENT_NOTIFICATION);
2✔
453
        return $this->_keepPolling;
2✔
454
    }
455

456
    /**
457
     * @param $message
458
     *
459
     * @throws Exception
460
     *
461
     * @return bool|mixed|string
462
     */
463
    protected function decrypt($message)
464
    {
465

466
        if (!$this->subscribed()) {
2✔
467
            throw new Exception('No subscription');
×
468
        }
469

470
        if ($this->_subscription['deliveryMode']['encryption'] && $this->_subscription['deliveryMode']['encryptionKey']) {
2✔
471

472
            $aes = new PubNubCrypto($this->_subscription['deliveryMode']['encryptionKey'], false);
1✔
473

474
            $message = $aes->unPadPKCS7(
1✔
475
                openssl_decrypt(
1✔
476
                    base64_decode($message),
1✔
477
                    'AES-128-ECB',
1✔
478
                    base64_decode($this->_subscription['deliveryMode']['encryptionKey']),
1✔
479
                    OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
1✔
480
                ),
1✔
481
                128
1✔
482
            );
1✔
483

484
            $message = Utils::json_parse($message, true); // PUBNUB itself always decode as array
1✔
485

486
        }
487

488
        return $message;
2✔
489

490
    }
491

492
    /**
493
     * @return array
494
     */
495
    protected function getFullEventFilters()
496
    {
497
        $events = [];
11✔
498
        foreach ($this->_eventFilters as $event) {
11✔
499
            $events[] = $this->_platform->createUrl($event);
11✔
500
        }
501
        return $events;
11✔
502
    }
503

504

505
}
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