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

ringcentral / ringcentral-php / 5130932145

pending completion
5130932145

push

github

web-flow
feat: websocket subscription support (#122)

* feat: support websocket subscription

* chore: update webhook demo

* misc: use Exception

* chore: update phpunit.xml

* fix: syntax error at php 7.4

* misc: refactor and add tests for websocket

* chore: require react/async at dev

* chore: add tests for ws subscription

* chore: add more tests

* chore: add more tests

216 of 216 new or added lines in 11 files covered. (100.0%)

664 of 732 relevant lines covered (90.71%)

30.7 hits per line

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

95.83
/src/WebSocket/WebSocket.php
1
<?php
2

3
namespace RingCentral\SDK\WebSocket;
4

5
use Exception;
6
use Symfony\Component\EventDispatcher\EventDispatcher;
7
use Ratchet\Client\Connector;
8
use RingCentral\SDK\Core\Utils;
9
use RingCentral\SDK\Platform\Platform;
10
use RingCentral\SDK\WebSocket\ApiRequest;
11
use RingCentral\SDK\WebSocket\ApiResponse;
12
use RingCentral\SDK\WebSocket\Events\ErrorEvent;
13
use RingCentral\SDK\WebSocket\Events\NotificationEvent;
14
use RingCentral\SDK\WebSocket\Events\CloseEvent;
15
use RingCentral\SDK\WebSocket\Events\SuccessEvent;
16

17
class WebSocket extends EventDispatcher
18
{
19
    const EVENT_READY = 'ready';
20
    const EVENT_NOTIFICATION = 'notification';
21
    const EVENT_CLOSE = 'close';
22
    const EVENT_ERROR = 'error';
23

24
    /** @var Platform */
25
    protected $_platform;
26

27
    /** @var WebSocket */
28
    protected $_connection;
29

30
    /** @var bool */
31
    protected $_ready;
32

33
    protected $_connectionDetails;
34

35
    protected $_wsToken;
36

37
    /** @var array */
38
    protected $_responseCallbacks = [];
39

40
    function __construct(Platform $platform)
41
    {
42
        $this->_platform = $platform;
30✔
43
        $this->_ready = false;
30✔
44
        $this->_responseCallbacks = [];
30✔
45
    }
46

47
    public function connect(Connector $connector = null) {
48
        try {
49
            return $this->_connect($connector);
18✔
50
        } catch (Exception $e) {
3✔
51
            $this->dispatch(new ErrorEvent($e), self::EVENT_ERROR);
3✔
52
            throw $e;
3✔
53
        }
54
    }
55

56
    protected function _connect(Connector $connector = null) {
57
        $tokenResponse = $this->_platform->post('/restapi/oauth/wstoken');
18✔
58
        $this->_wsToken = $tokenResponse->json();
15✔
59
        $authHeaders = [
15✔
60
            'Authorization' => 'Bearer ' . $this->_wsToken->ws_access_token
15✔
61
        ];
15✔
62
        if (null === $connector) {
15✔
63
            $connector = new Connector();
×
64
        }
65
        return $connector($this->_wsToken->uri, [], $authHeaders)->then(function($conn) {
15✔
66
            $this->_connection = $conn;
12✔
67
            $this->_connection->on('message', function($msg) {
12✔
68
                $this->handleMessages($msg);
9✔
69
            });
12✔
70
            $this->_connection->on('close', function($code = null, $reason = null) {
12✔
71
                $this->clear();
3✔
72
                $this->dispatch(new CloseEvent($code, $reason), self::EVENT_CLOSE);
3✔
73
            });
12✔
74
            $this->_connection->on('error', function(Exception $e) {
12✔
75
                $this->clear();
3✔
76
                $this->dispatch(new ErrorEvent($e), self::EVENT_ERROR);
3✔
77
            });
12✔
78
        }, function ($e) {
15✔
79
            $this->clear();
3✔
80
            $this->dispatch(new ErrorEvent($e), self::EVENT_ERROR);
3✔
81
        });
15✔
82
    }
83

84
    public function send(string $data) {
85
        if (!$this->ready()) {
12✔
86
            $readyCallback = function() use ($data, &$readyCallback) {
3✔
87
                $this->removeListener(self::EVENT_READY, $readyCallback);
3✔
88
                $readyCallback = null;
3✔
89
                $this->_connection->send($data);
3✔
90
            };
3✔
91
            $this->addListener(self::EVENT_READY, $readyCallback);
3✔
92
        } else {
93
            $this->_connection->send($data);
9✔
94
        }
95
    }
96

97
    public function sendRequest(ApiRequest $request, $responseCallback) {
98
        $this->_responseCallbacks[$request->requestId()] = $responseCallback;
6✔
99
        $this->send($request->toJSON());
6✔
100
    }
101

102
    public function close() {
103
        if ($this->_connection) {
6✔
104
            $this->_connection->close();
×
105
            $this->clear();
×
106
        }
107
    }
108

109
    protected function handleMessages($msg) {
110
        $data = Utils::json_parse($msg, true);
9✔
111
        $response = $data[0];
9✔
112
        $body = null;
9✔
113
        if (isset($data[1])) {
9✔
114
            $body = $data[1];
9✔
115
        }
116
        if ($response['type'] === 'ConnectionDetails') {
9✔
117
            if (empty($this->_connectionDetails)) {
9✔
118
                $this->_connectionDetails = new ApiResponse($response, $body);
9✔
119
            } else {
120
                if (empty($response['wsc'])) {
3✔
121
                    $response['wsc'] = $this->_connectionDetails->wsc();
3✔
122
                }
123
                if (empty($body)) {
3✔
124
                    $body = $this->_connectionDetails->body();
3✔
125
                }
126
                $this->_connectionDetails = new ApiResponse($response, $body);
3✔
127
            }
128
            $this->_ready = true;
9✔
129
            $this->dispatch(new SuccessEvent($this->_connectionDetails), self::EVENT_READY);
9✔
130
            return;
9✔
131
        }
132
        if ($response['type'] === 'ServerNotification') {
9✔
133
            $this->dispatch(new NotificationEvent($body), self::EVENT_NOTIFICATION);
6✔
134
            return;
6✔
135
        }
136
        $apiResponse = new ApiResponse($response, $body);
6✔
137
        $requestId = $apiResponse->requestId();
6✔
138
        if (isset($this->_responseCallbacks[$requestId])) {
6✔
139
            $this->_responseCallbacks[$requestId]($apiResponse);
6✔
140
            unset($this->_responseCallbacks[$requestId]);
6✔
141
        }
142
    }
143

144
    protected function clear()
145
    {
146
        $this->_ready = false;
6✔
147
        $this->_connection = null;
6✔
148
    }
149

150
    public function ready()
151
    {
152
        return $this->_ready;
12✔
153
    }
154

155
    public function connection()
156
    {
157
        return $this->_connection;
3✔
158
    }
159
}
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