Passed
Push — master ( 2023c3...93feda )
by Jacobo
03:27 queued 56s
created

Session::fromArray()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 6
eloc 17
c 3
b 0
f 0
nc 4
nop 1
dl 0
loc 26
rs 9.0777
1
<?php
2
3
namespace SquareetLabs\LaravelOpenVidu;
4
5
use Exception;
6
use GuzzleHttp\Client;
7
use GuzzleHttp\RequestOptions;
8
use Illuminate\Support\Facades\Cache;
9
use JsonSerializable;
10
use SquareetLabs\LaravelOpenVidu\Builders\ConnectionBuilder;
11
use SquareetLabs\LaravelOpenVidu\Builders\PublisherBuilder;
12
use SquareetLabs\LaravelOpenVidu\Builders\SessionPropertiesBuilder;
13
use SquareetLabs\LaravelOpenVidu\Enums\MediaMode;
14
use SquareetLabs\LaravelOpenVidu\Enums\OpenViduRole;
15
use SquareetLabs\LaravelOpenVidu\Enums\OutputMode;
16
use SquareetLabs\LaravelOpenVidu\Enums\RecordingLayout;
17
use SquareetLabs\LaravelOpenVidu\Enums\RecordingMode;
18
use SquareetLabs\LaravelOpenVidu\Enums\Uri;
19
use SquareetLabs\LaravelOpenVidu\Exceptions\OpenViduConnectionNotFoundException;
20
use SquareetLabs\LaravelOpenVidu\Exceptions\OpenViduException;
21
use SquareetLabs\LaravelOpenVidu\Exceptions\OpenViduSessionNotFoundException;
22
use SquareetLabs\LaravelOpenVidu\Exceptions\OpenViduTokenCantCreateException;
23
24
/**
25
 * Class Session
26
 * @package SquareetLabs\LaravelOpenVidu
27
 */
28
class Session implements JsonSerializable
29
{
30
    /** @var  Client */
31
    private $client;
32
33
    /** @var  string */
34
    private $sessionId;
35
36
    /** @var SessionProperties */
37
    private $properties;
38
39
    /** @var bool */
40
    private $recording;
41
42
    /** @var array */
43
    private $activeConnections = [];
44
45
    /** @var int */
46
    private $createdAt;
47
48
    /**
49
     * Session constructor.
50
     * @param Client $client
51
     * @param SessionProperties|null $properties
52
     * @throws OpenViduException
53
     */
54
    public function __construct(Client $client, ?SessionProperties $properties = null)
55
    {
56
        $this->client = $client;
57
        $this->properties = $properties ? $properties : new SessionProperties(MediaMode::ROUTED, RecordingMode::MANUAL, OutputMode::COMPOSED, RecordingLayout::BEST_FIT);
58
        $this->sessionId = $this->getSessionId();
59
    }
60
61
    /**
62
     * @return string
63
     * @throws OpenViduException
64
     */
65
    public function getSessionId()
66
    {
67
        if (empty($this->sessionId)) {
68
            $response = $this->client->post(Uri::SESSION_URI, [
69
                RequestOptions::JSON => $this->properties->toArray()
70
            ]);
71
            switch ($response->getStatusCode()) {
72
                case 200:
73
                    return json_decode($response->getBody()->getContents())->id;
74
                case 409:
75
                    return $this->properties->getCustomSessionId();
76
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
77
                default:
78
                    throw new OpenViduException("Invalid response status code " . $response->getStatusCode(), $response->getStatusCode());
79
            }
80
        } else {
81
            return $this->sessionId;
82
        }
83
    }
84
85
    /**
86
     ** Gets a new token associated to Session object with default values for
87
     * {@see TokenOptions}. This always translates into a
88
     * new request to OpenVidu Server
89
     *
90
     * @param TokenOptions|null $tokenOptions
91
     * @return string The generated token
92
     * @throws OpenViduException
93
     */
94
    public function generateToken(?TokenOptions $tokenOptions = null)
95
    {
96
        $this->getSessionId();
97
        try {
98
            if (!$tokenOptions) {
99
                $tokenOptions = new TokenOptions(OpenViduRole::PUBLISHER);;
100
            }
101
            $response = $this->client->post(Uri::TOKEN_URI, [
102
                RequestOptions::JSON => array_merge($tokenOptions->toArray(), ['session' => $this->sessionId])
103
            ]);
104
            return json_decode($response->getBody()->getContents());
105
        } catch (Exception $e) {
106
            throw new OpenViduTokenCantCreateException($e->getMessage(), $e);
107
        }
108
    }
109
110
    /**
111
     * Gracefully closes the Session: unpublishes all streams and evicts every
112
     * participant
113
     * @throws OpenViduException
114
     */
115
    public function close()
116
    {
117
        $response = $this->client->delete(Uri::SESSION_URI . '/' . $this->sessionId);
118
        switch ($response->getStatusCode()) {
119
            case 204:
120
                Cache::store('openvidu')->forget($this->sessionId);
121
                break;
122
            case 404:
123
                throw new OpenViduSessionNotFoundException();
124
                break;
125
            default:
126
                throw new OpenViduException("Invalid response status code " . $response->getStatusCode(), $response->getStatusCode());
127
        }
128
    }
129
130
    /**
131
     * Updates every property of the Session with the current status it has in
132
     * OpenVidu Server. This is especially useful for getting the list of active
133
     * connections to the Session
134
     * ({@see getActiveConnections()}) and use
135
     * those values to call
136
     * {@see forceDisconnect(Connection)} or
137
     * {@link forceUnpublish(Publisher)}. <br>
138
     *
139
     * To update every Session object owned by OpenVidu object, call
140
     * {@see fetch()}
141
     *
142
     * @return bool true if the Session status has changed with respect to the server,
143
     * false if not. This applies to any property or sub-property of the object
144
     */
145
146
    public function fetch()
147
    {
148
        $response = $this->client->get(Uri::SESSION_URI . '/' . $this->sessionId, [
149
            'headers' => [
150
                'Content-Type' => 'application/x-www-form-urlencoded',
151
                'Accept' => 'application/json',
152
            ]
153
        ]);
154
        if ($response->getStatusCode() === 200) {
155
            $beforeJSON = $this->toJson();
156
            $this->fromJson($response->getBody()->getContents());
157
            $afterJSON = $this->toJson();
158
            if ($beforeJSON !== $afterJSON) {
159
                Cache::store('openvidu')->update($this->sessionId, $this->toJson());
160
                return true;
161
            }
162
        }
163
        return false;
164
    }
165
166
    /**
167
     * Convert the model instance to JSON.
168
     *
169
     * @param int $options
170
     * @return string
171
     *
172
     */
173
    public function toJson($options = 0): string
174
    {
175
        $json = json_encode($this->jsonSerialize(), $options);
176
        return $json;
177
    }
178
179
    /**
180
     * Specify data which should be serialized to JSON
181
     * @link https://php.net/manual/en/jsonserializable.jsonserialize.php
182
     * @return mixed data which can be serialized by <b>json_encode</b>,
183
     * which is a value of any type other than a resource.
184
     * @since 5.4.0
185
     */
186
    public function jsonSerialize()
187
    {
188
        return $this->toArray();
189
    }
190
191
    /**
192
     * Convert the model instance to an array.
193
     *
194
     * @return array
195
     */
196
    public function toArray(): array
197
    {
198
        $array = ['sessionId' => $this->sessionId, 'properties' => $this->properties->toArray(), 'recording' => $this->recording, 'createdAt' => $this->createdAt];
199
        foreach ($this->activeConnections as $connection) {
200
            $array['activeConnections'][] = $connection->toArray();
201
        }
202
203
        foreach ($array as $key => $value) {
204
            if (is_null($value) || $value == '')
205
                unset($array[$key]);
206
        }
207
        return $array;
208
    }
209
210
    /**
211
     * @param string $json
212
     * @return Session
213
     */
214
    public function fromJson(string $json): Session
215
    {
216
        return $this->fromArray(json_decode($json, true));
217
    }
218
219
    /**
220
     * @param array $sessionArray
221
     * @return Session
222
     */
223
    public function fromArray(array $sessionArray): Session
224
    {
225
        $this->sessionId = $sessionArray['sessionId'];
226
        $this->createdAt = $sessionArray['createdAt'] ?? null;
227
        $this->recording = $sessionArray['recording'] ?? null;
228
229
        if (array_key_exists('properties', $sessionArray)) {
230
            $this->properties = SessionPropertiesBuilder::build($sessionArray['properties']);
231
        }
232
233
        $this->activeConnections = [];
234
        if (array_key_exists('connections', $sessionArray)) {
235
            foreach ($sessionArray['connections'] as $connection) {
236
                $publishers = [];
237
                $ensure = $connection['content'] ?? $connection;
238
                foreach ($ensure['publishers'] as $publisher) {
239
                    $publishers[] = PublisherBuilder::build($publisher);
240
                }
241
                $subscribers = [];
242
                foreach ($ensure->subscribers as $subscriber) {
243
                    $subscribers[] = $subscriber->streamId;
244
                }
245
                $this->activeConnections[] = ConnectionBuilder::build($ensure, $publishers, $subscribers);
246
            }
247
        }
248
        return $this;
249
    }
250
251
    /**
252
     * Forces the user with Connection `connectionId` to leave the session. OpenVidu Browser will trigger the proper events on the client-side
253
     * (`streamDestroyed`, `connectionDestroyed`, `sessionDisconnected`) with reason set to `"forceDisconnectByServer"`
254
     *
255
     *
256
     * @param string $connectionId
257
     * @return bool
258
     * @throws OpenViduException
259
     */
260
    public function forceDisconnect(string $connectionId): bool
261
    {
262
        $response = $this->client->delete(Uri::SESSION_URI . '/' . $this->sessionId . '/connection/' . $connectionId, [
263
            'headers' => [
264
                'Content-Type' => 'application/x-www-form-urlencoded',
265
                'Accept' => 'application/json',
266
            ]
267
        ]);
268
        switch ($response->getStatusCode()) {
269
            case 204:
270
                $this->leaveSession($connectionId);
271
                Cache::store('openvidu')->update($this->sessionId, $this->toJson());
272
                break;
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return boolean. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
273
            case 400:
274
                throw new OpenViduSessionNotFoundException();
275
                break;
276
            case 404:
277
                throw new OpenViduConnectionNotFoundException();
278
                break;
279
            default:
280
                throw new OpenViduException("Invalid response status code " . $response->getStatusCode(), $response->getStatusCode());
281
        }
282
    }
283
284
    /**
285
     * Get `connection` parameter from activeConnections array {@see Connection::getConnectionId()} for getting each `connectionId` property).
286
     * Remember to call {@see fetch()} before to fetch the current actual properties of the Session from OpenVidu Server
287
     * @param string $connectionId
288
     */
289
290
    private function leaveSession(string $connectionId)
291
    {
292
        $connectionClosed = null;
293
        $this->activeConnections = array_filter($this->activeConnections, function (Connection $connection) use (&$connectionClosed, $connectionId) {
294
            if ($connection->getConnectionId() !== $connectionId) {
295
                return true;
296
            }
297
            $connectionClosed = $connection;
298
            return false;
299
        });
300
        if ($connectionClosed != null) {
301
            foreach ($connectionClosed->getPublishers() as $publisher) {
302
                foreach ($this->activeConnections as $con) {
303
                    $con->unsubscribe($publisher->getStreamId());
304
                }
305
            }
306
        }
307
    }
308
309
    /**
310
     * Forces some user to unpublish a Stream. OpenVidu Browser will trigger the
311
     * proper events on the client-side (<code>streamDestroyed</code>) with reason
312
     * set to "forceUnpublishByServer". <br>
313
     *
314
     * You can get <code>streamId</code> parameter with
315
     * {@see Session::getActiveConnections()} and then for
316
     * each Connection you can call
317
     * {@see  Connection::getPublishers()}. Finally
318
     * {@see Publisher::getStreamId()}) will give you the
319
     * <code>streamId</code>. Remember to call
320
     * {@see fetch()} before to fetch the current
321
     * actual properties of the Session from OpenVidu Server
322
     *
323
     * @param string $streamId
324
     * @return void
325
     * @throws OpenViduConnectionNotFoundException
326
     * @throws OpenViduException
327
     * @throws OpenViduSessionNotFoundException
328
     */
329
    public function forceUnpublish(string $streamId)
330
    {
331
        $response = $this->client->delete(Uri::SESSION_URI . '/' . $this->sessionId . '/stream/' . $streamId, [
332
            'headers' => [
333
                'Content-Type' => 'application/x-www-form-urlencoded',
334
                'Accept' => 'application/json',
335
            ]
336
        ]);
337
        switch ($response->getStatusCode()) {
338
            case 204:
339
                foreach ($this->activeConnections as $connection) {
340
                    $connection->unpublish($streamId);
341
                    $connection->unsubscribe($streamId);
342
                }
343
                Cache::store('openvidu')->update($this->sessionId, $this->toJson());
344
                break;
345
            case 400:
346
                throw new OpenViduSessionNotFoundException();
347
                break;
348
            case 404:
349
                throw new OpenViduConnectionNotFoundException();
350
                break;
351
            default:
352
                throw new OpenViduException("Invalid response status code " . $response->getStatusCode(), $response->getStatusCode());
353
        }
354
    }
355
356
    /**
357
     * Returns the list of active connections to the session. <strong>This value
358
     * will remain unchanged since the last time method
359
     * {@see fetch()} was called</strong>.
360
     * Exceptions to this rule are:
361
     * <ul>
362
     * <li>Calling {@see Session::forceUnpublish(string)}
363
     * updates each affected Connection status</li>
364
     * <li>Calling {@see Session::forceDisconnect(string)}
365
     * updates each affected Connection status</li>
366
     * </ul>
367
     * <br>
368
     * To get the list of active connections with their current actual value, you
369
     * must call first {@see Session::fetch()} and then
370
     * {@see Session::getActiveConnections()}
371
     */
372
    public function getActiveConnections(): array
373
    {
374
        return $this->activeConnections;
375
    }
376
377
    /**
378
     * Returns whether the session is being recorded or not
379
     */
380
    public function isBeingRecorded(): bool
381
    {
382
        return $this->recording;
383
    }
384
385
    /**
386
     * Set value
387
     * @param bool $recording
388
     */
389
    public function setIsBeingRecorded(bool $recording)
390
    {
391
        $this->recording = $recording;
392
        Cache::store('openvidu')->update($this->sessionId, $this->toJson());
393
    }
394
395
    /**
396
     * @return string
397
     * @throws OpenViduException
398
     */
399
    public function __toString(): string
400
    {
401
        return $this->getSessionId();
402
    }
403
}
404