Channels   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 618
Duplicated Lines 23.95 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 1.51%

Importance

Changes 0
Metric Value
wmc 49
lcom 1
cbo 7
dl 148
loc 618
ccs 3
cts 199
cp 0.0151
rs 8.462
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A getChannels() 12 12 2
A createChannel() 40 40 2
A getChannel() 11 11 2
A createChannelWithId() 39 39 2
A deleteChannel() 0 9 2
A hangup() 0 8 2
A continueDialplan() 0 15 2
A redirect() 0 13 2
A answer() 0 9 2
A startRinging() 0 9 2
A stopRinging() 0 9 2
A sendDtmf() 0 17 2
A mute() 0 13 2
A unmute() 9 9 2
A hold() 0 9 2
A unhold() 0 9 2
A startSilence() 0 9 2
A stopSilence() 0 9 2
A getChannelVar() 0 23 4
A getVariable() 0 4 1
A setChannelVar() 0 16 2
A setVariable() 0 4 1
A startSnoop() 19 19 2
A startSnoopWithId() 18 18 2
A getType() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Channels often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Channels, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * Copyright 2014 Brian Smith <[email protected]>.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *      http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace phparia\Api;
20
21
use GuzzleHttp\Exception\RequestException;
22
use phparia\Exception\MissingParameterException;
23
use phparia\Exception\PreconditionFailedException;
24
use phparia\Exception\UnprocessableEntityException;
25
use phparia\Resources\Channel;
26
use phparia\Resources\Variable;
27
use phparia\Exception\ConflictException;
28
use phparia\Exception\InvalidParameterException;
29
use phparia\Exception\NotFoundException;
30
use phparia\Exception\ServerException;
31
32
/**
33
 * Channels API
34
 *
35
 * @author Brian Smith <[email protected]>
36
 */
37
class Channels extends MediaBase
38
{
39
    const AST_STATE_DOWN = 'Down'; // Channel is down and available
40
    const AST_STATE_RESERVED = 'Rsrvd'; // Channel is down, but reserved
41
    const AST_STATE_OFFHOOK = 'OffHook'; // Channel is off hook
42
    const AST_STATE_DIALING = 'Dialing'; // Digits (or equivalent) have been dialed
43
    const AST_STATE_RING = 'Ring'; // Line is ringing
44
    const AST_STATE_RINGING = 'Ringing'; // Remote end is ringing
45
    const AST_STATE_UP = 'Up'; // Line is up
46
    const AST_STATE_BUSY = 'Busy'; // Line is busy
47
    const AST_STATE_DIALING_OFFHOOK = 'Dialing Offhook'; // Digits (or equivalent) have been dialed while offhook
48
    const AST_STATE_PRERING = 'Pre-ring'; // Channel has detected an incoming call and is waiting for ring
49
    const AST_STATE_MUTE = 'Mute'; // Do not transmit voice data
50
    const AST_STATE_UNKNOWN = 'Unknown';
51
52
    /**
53
     * List all active channels in Asterisk.
54
     *
55
     * @return Channel[]
56
     */
57 88 View Code Duplication
    public function getChannels()
58
    {
59 88
        $uri = 'channels';
60 88
        $response = $this->client->getEndpoint()->get($uri);
61
62
        $channels = [];
63
        foreach (\GuzzleHttp\json_decode($response->getBody()) as $channel) {
64
            $channels[] = new Channel($this->client, $channel);
65
        }
66
67
        return $channels;
68
    }
69
70
    /**
71
     * Create a new channel (originate). The new channel is created immediately and a snapshot of it
72
     * returned. If a Stasis application is provided it will be automatically subscribed to the originated
73
     * channel for further events and updates.
74
     *
75
     * @param string $endpoint (required) Endpoint to call.
76
     * @param string $extension The extension to dial after the endpoint answers
77
     * @param string $context The context to dial after the endpoint answers. If omitted, uses 'default'
78
     * @param int $priority The priority to dial after the endpoint answers. If omitted, uses 1
79
     * @param string $label Asterisk 13+ The label to dial after the endpoint answers. Will supersede 'priority' if provided. Mutually exclusive with 'app'.
80
     * @param string $app The application that is subscribed to the originated channel. When the channel is answered, it will be passed to this Stasis application. Mutually exclusive with 'context', 'extension', 'priority', and 'label'.
81
     * @param string $appArgs The application arguments to pass to the Stasis application.
82
     * @param string $callerId CallerID to use when dialing the endpoint or extension.
83
     * @param int $timeout (default 30) Timeout (in seconds) before giving up dialing, or -1 for no timeout.
84
     * @param string $channelId The unique id to assign the channel on creation.
85
     * @param string $otherChannelId The unique id to assign the second channel when using local channels.
86
     * @param string $formats The format name capability list to use if originator is not specified. Ex. "ulaw,slin16". Format names can be found with "core show codecs".
87
     * @param array $variables The "variables" key in the body object holds variable key/value pairs to set on the channel on creation. Other keys in the body object are interpreted as query parameters. Ex. { "endpoint": "SIP/Alice", "variables": { "CALLERID(name)": "Alice" } }
88
     * @return Channel
89
     * @throws InvalidParameterException
90
     * @throws ServerException
91
     */
92 View Code Duplication
    public function createChannel(
93
        $endpoint,
94
        $extension = null,
95
        $context = null,
96
        $priority = null,
97
        $label = null,
98
        $app = null,
99
        $appArgs = null,
100
        $callerId = null,
101
        $timeout = null,
102
        $channelId = null,
103
        $otherChannelId = null,
104
        $formats = null,
105
        $variables = array()
106
    ) {
107
        $uri = 'channels';
108
        try {
109
            $response = $this->client->getEndpoint()->post($uri, [
110
                'form_params' => [
111
                    'endpoint' => $endpoint,
112
                    'extension' => $extension,
113
                    'context' => $context,
114
                    'priority' => $priority,
115
                    'label' => $label,
116
                    'app' => $app,
117
                    'appArgs' => $appArgs,
118
                    'callerId' => $callerId,
119
                    'timeout' => $timeout,
120
                    'channelId' => $channelId,
121
                    'otherChannelId' => $otherChannelId,
122
                    'formats' => $formats,
123
                    'variables' => array_map('strval', $variables),
124
                ]
125
            ]);
126
        } catch (RequestException $e) {
127
            $this->processRequestException($e);
128
        }
129
130
        return new Channel($this->client, \GuzzleHttp\json_decode($response->getBody()));
131
    }
132
133
    /**
134
     * Channel details.
135
     *
136
     * @param string $channelId
137
     * @return Channel
138
     * @throws NotFoundException
139
     */
140 View Code Duplication
    public function getChannel($channelId)
141
    {
142
        $uri = "channels/$channelId";
143
        try {
144
            $response = $this->client->getEndpoint()->get($uri);
145
        } catch (RequestException $e) {
146
            $this->processRequestException($e);
147
        }
148
149
        return new Channel($this->client, \GuzzleHttp\json_decode($response->getBody()));
150
    }
151
152
    /**
153
     * Create a new channel (originate). The new channel is created immediately and a snapshot of it
154
     * returned. If a Stasis application is provided it will be automatically subscribed to the originated
155
     * channel for further events and updates.
156
     *
157
     * @param string $endpoint (required) Endpoint to call.
158
     * @param string $extension The extension to dial after the endpoint answers
159
     * @param string $context The context to dial after the endpoint answers. If omitted, uses 'default'
160
     * @param int $priority The priority to dial after the endpoint answers. If omitted, uses 1
161
     * @param string $label Asterisk 13+ The label to dial after the endpoint answers. Will supersede 'priority' if provided. Mutually exclusive with 'app'.
162
     * @param string $app The application that is subscribed to the originated channel, and passed to the Stasis application.
163
     * @param string $appArgs The application arguments to pass to the Stasis application.
164
     * @param string $callerId CallerID to use when dialing the endpoint or extension.
165
     * @param int $timeout (default 30) Timeout (in seconds) before giving up dialing, or -1 for no timeout.
166
     * @param string $channelId The unique id to assign the channel on creation.
167
     * @param string $otherChannelId The unique id to assign the second channel when using local channels.
168
     * @param string $formats The format name capability list to use if originator is not specified. Ex. "ulaw,slin16". Format names can be found with "core show codecs".
169
     * @param array $variables The "variables" key in the body object holds variable key/value pairs to set on the channel on creation. Other keys in the body object are interpreted as query parameters. Ex. { "endpoint": "SIP/Alice", "variables": { "CALLERID(name)": "Alice" } }
170
     * @return Channel
171
     * @throws InvalidParameterException
172
     * @throws ServerException
173
     */
174 View Code Duplication
    public function createChannelWithId(
175
        $endpoint,
176
        $extension = null,
177
        $context = null,
178
        $priority = null,
179
        $label = null,
180
        $app = null,
181
        $appArgs = null,
182
        $callerId = null,
183
        $timeout = null,
184
        $channelId = null,
185
        $otherChannelId = null,
186
        $formats = null,
187
        $variables = array()
188
    ) {
189
        $uri = "channels/$channelId";
190
        try {
191
            $response = $this->client->getEndpoint()->post($uri, [
192
                'form_params' => [
193
                    'endpoint' => $endpoint,
194
                    'extension' => $extension,
195
                    'context' => $context,
196
                    'priority' => $priority,
197
                    'label' => $label,
198
                    'app' => $app,
199
                    'appArgs' => $appArgs,
200
                    'callerId' => $callerId,
201
                    'timeout' => $timeout,
202
                    'otherChannelId' => $otherChannelId,
203
                    'formats' => $formats,
204
                    'variables' => array_map('strval', $variables),
205
                ]
206
            ]);
207
        } catch (RequestException $e) {
208
            $this->processRequestException($e);
209
        }
210
211
        return new Channel($this->client, \GuzzleHttp\json_decode($response->getBody()));
212
    }
213
214
    /**
215
     * Delete (i.e. hangup) a channel.
216
     *
217
     * @param string $channelId Channel's id
218
     * @throws NotFoundException
219
     */
220
    public function deleteChannel($channelId)
221
    {
222
        $uri = "channels/$channelId";
223
        try {
224
            $this->client->getEndpoint()->delete($uri);
225
        } catch (RequestException $e) {
226
            $this->processRequestException($e);
227
        }
228
    }
229
230
    /**
231
     * Hangup a channel if it still exists.
232
     *
233
     * @param string $channelId Channel's id
234
     */
235
    public function hangup($channelId)
236
    {
237
        try {
238
            $this->deleteChannel($channelId);
239
        } catch (\Exception $ignore) {
240
            // Don't throw exception if the channel doesn't exist
241
        }
242
    }
243
244
    /**
245
     * Exit application; continue execution in the dialplan.
246
     *
247
     * @param string $channelId Channel's id
248
     * @param string $context The context to continue to.
249
     * @param string $extension The extension to continue to.
250
     * @param int $priority The priority to continue to.
251
     * @throws NotFoundException
252
     * @throws ConflictException
253
     */
254
    public function continueDialplan($channelId, $context, $extension, $priority)
255
    {
256
        $uri = "channels/$channelId/continue";
257
        try {
258
            $this->client->getEndpoint()->post($uri, [
259
                'form_params' => [
260
                    'context' => $context,
261
                    'extension' => $extension,
262
                    'priority' => $priority,
263
                ]
264
            ]);
265
        } catch (RequestException $e) {
266
            $this->processRequestException($e);
267
        }
268
    }
269
270
    /**
271
     * Redirect the channel to a different location.
272
     *
273
     * @param string $channelId Channel's id
274
     * @param string $endpoint (required) The endpoint to redirect the channel to
275
     * @throws MissingParameterException
276
     * @throws NotFoundException
277
     * @throws ConflictException
278
     * @throws UnprocessableEntityException
279
     * @throws PreconditionFailedException
280
     */
281
    public function redirect($channelId, $endpoint)
282
    {
283
        $uri = "channels/$channelId/redirect";
284
        try {
285
            $this->client->getEndpoint()->post($uri, [
286
                'form_params' => [
287
                    'endpoint' => $endpoint
288
                ]
289
            ]);
290
        } catch (RequestException $e) {
291
            $this->processRequestException($e);
292
        }
293
    }
294
295
    /**
296
     * Answer a channel.
297
     *
298
     * @param string $channelId Channel's id
299
     * @throws NotFoundException
300
     * @throws ConflictException
301
     */
302
    public function answer($channelId)
303
    {
304
        $uri = "channels/$channelId/answer";
305
        try {
306
            $this->client->getEndpoint()->post($uri);
307
        } catch (RequestException $e) {
308
            $this->processRequestException($e);
309
        }
310
    }
311
312
    /**
313
     * Indicate ringing to a channel.
314
     *
315
     * @param string $channelId
316
     * @throws NotFoundException
317
     * @throws ConflictException
318
     */
319
    public function startRinging($channelId)
320
    {
321
        $uri = "channels/$channelId/ring";
322
        try {
323
            $this->client->getEndpoint()->post($uri);
324
        } catch (RequestException $e) {
325
            $this->processRequestException($e);
326
        }
327
    }
328
329
    /**
330
     * Stop ringing indication on a channel if locally generated.
331
     *
332
     * @param string $channelId
333
     * @throws NotFoundException
334
     * @throws ConflictException
335
     */
336
    public function stopRinging($channelId)
337
    {
338
        $uri = "channels/$channelId/ring";
339
        try {
340
            $this->client->getEndpoint()->delete($uri);
341
        } catch (RequestException $e) {
342
            $this->processRequestException($e);
343
        }
344
    }
345
346
    /**
347
     * Send provided DTMF to a given channel.
348
     *
349
     * @param string $channelId
350
     * @param string $dtmf DTMF To send.
351
     * @param int $before Amount of time to wait before DTMF digits (specified in milliseconds) start.
352
     * @param int $between Amount of time in between DTMF digits (specified in milliseconds).  Default: 100
353
     * @param int $duration Length of each DTMF digit (specified in milliseconds).  Default: 100
354
     * @param int $after Amount of time to wait after DTMF digits (specified in milliseconds) end.
355
     * @throws InvalidParameterException
356
     * @throws NotFoundException
357
     * @throws ConflictException
358
     */
359
    public function sendDtmf($channelId, $dtmf, $before = null, $between = null, $duration = null, $after = null)
360
    {
361
        $uri = "channels/$channelId/dtmf";
362
        try {
363
            $this->client->getEndpoint()->post($uri, [
364
                'form_params' => [
365
                    'dtmf' => $dtmf,
366
                    'before' => $before,
367
                    'between' => $between,
368
                    'duration' => $duration,
369
                    'after' => $after,
370
                ]
371
            ]);
372
        } catch (RequestException $e) {
373
            $this->processRequestException($e);
374
        }
375
    }
376
377
    /**
378
     * Mute a channel.
379
     *
380
     * @param string $channelId Channel's id
381
     * @param string $direction (default both) Direction in which to mute audio.  Allowed values: both, in, out
382
     * @throws NotFoundException
383
     * @throws ConflictException
384
     */
385
    public function mute($channelId, $direction)
386
    {
387
        $uri = "channels/$channelId/mute";
388
        try {
389
            $this->client->getEndpoint()->post($uri, [
390
                'form_params' => [
391
                    'direction' => $direction,
392
                ]
393
            ]);
394
        } catch (RequestException $e) {
395
            $this->processRequestException($e);
396
        }
397
    }
398
399
    /**
400
     * Unmute a channel.
401
     *
402
     * @param string $channelId Channel's id
403
     * @param string $direction (default both) Direction in which to unmute audio
404
     * @throws NotFoundException
405
     * @throws ConflictException
406
     */
407 View Code Duplication
    public function unmute($channelId, $direction)
408
    {
409
        $uri = "channels/$channelId/mute?direction=".\GuzzleHttp\json_encode($direction);
410
        try {
411
            $this->client->getEndpoint()->delete($uri);
412
        } catch (RequestException $e) {
413
            $this->processRequestException($e);
414
        }
415
    }
416
417
    /**
418
     * Hold a channel.
419
     *
420
     * @param string $channelId Channel's id
421
     * @throws NotFoundException
422
     * @throws ConflictException
423
     */
424
    public function hold($channelId)
425
    {
426
        $uri = "channels/$channelId/hold";
427
        try {
428
            $this->client->getEndpoint()->post($uri);
429
        } catch (RequestException $e) {
430
            $this->processRequestException($e);
431
        }
432
    }
433
434
    /**
435
     * Remove a channel from hold.
436
     *
437
     * @param string $channelId Channel's id
438
     * @throws NotFoundException
439
     * @throws ConflictException
440
     */
441
    public function unhold($channelId)
442
    {
443
        $uri = "channels/$channelId/hold";
444
        try {
445
            $this->client->getEndpoint()->delete($uri);
446
        } catch (RequestException $e) {
447
            $this->processRequestException($e);
448
        }
449
    }
450
451
    /**
452
     * Play silence to a channel. Using media operations such as /play on a channel playing silence in this manner will suspend silence without resuming automatically.
453
     *
454
     * @param string $channelId Channel's id
455
     * @throws NotFoundException
456
     * @throws ConflictException
457
     */
458
    public function startSilence($channelId)
459
    {
460
        $uri = "channels/$channelId/silence";
461
        try {
462
            $this->client->getEndpoint()->post($uri);
463
        } catch (RequestException $e) {
464
            $this->processRequestException($e);
465
        }
466
    }
467
468
    /**
469
     * Stop playing silence to a channel.
470
     *
471
     * @param string $channelId Channel's id
472
     * @throws NotFoundException
473
     * @throws ConflictException
474
     */
475
    public function stopSilence($channelId)
476
    {
477
        $uri = "channels/$channelId/silence";
478
        try {
479
            $this->client->getEndpoint()->delete($uri);
480
        } catch (RequestException $e) {
481
            $this->processRequestException($e);
482
        }
483
    }
484
485
    /**
486
     * Get the value of a channel variable or function.
487
     *
488
     * @param string $channelId
489
     * @param string $variable
490
     * @param null|string $default The value to return if the variable does not exist
491
     * @return string|Variable
492
     * @throws ConflictException
493
     * @throws InvalidParameterException
494
     * @throws NotFoundException
495
     */
496
    public function getChannelVar($channelId, $variable, $default = null)
497
    {
498
        $uri = "channels/$channelId/variable";
499
        try {
500
            $response = $this->client->getEndpoint()->get($uri, [
501
                'form_params' => [
502
                    'variable' => $variable,
503
                ]
504
            ]);
505
        } catch (RequestException $e) {
506
            try {
507
                $this->processRequestException($e);
508
            } catch (NotFoundException $notFoundException) {
509
                if ($default === null) {
510
                    throw $notFoundException;
511
                }
512
513
                return $default;
514
            }
515
        }
516
517
        return new Variable(\GuzzleHttp\json_decode($response->getBody()));
518
    }
519
520
    /**
521
     * Get the value of a channel variable or function.
522
     *
523
     * @param string $channelId
524
     * @param string $variable
525
     * @param null|string $default The value to return if the variable does not exist
526
     * @return string|Variable
527
     * @throws ConflictException
528
     * @throws InvalidParameterException
529
     * @throws NotFoundException
530
     * @deprecated
531
     */
532
    public function getVariable($channelId, $variable, $default = null)
533
    {
534
        return $this->getChannelVar($channelId, $variable, $default);
535
    }
536
537
    /**
538
     * Set the value of a channel variable or function.
539
     *
540
     * @param string $channelId
541
     * @param string $variable
542
     * @param string $value
543
     * @return Variable
544
     * @throws InvalidParameterException
545
     * @throws NotFoundException
546
     * @throws ConflictException
547
     */
548
    public function setChannelVar($channelId, $variable, $value)
549
    {
550
        $uri = "channels/$channelId/variable";
551
        try {
552
            $response = $this->client->getEndpoint()->post($uri, [
553
                'form_params' => [
554
                    'variable' => $variable,
555
                    'value' => $value,
556
                ]
557
            ]);
558
        } catch (RequestException $e) {
559
            $this->processRequestException($e);
560
        }
561
562
        return new Variable(\GuzzleHttp\json_decode($response->getBody()));
563
    }
564
565
    /**
566
     * Set the value of a channel variable or function.
567
     *
568
     * @param string $channelId
569
     * @param string $variable
570
     * @param string $value
571
     * @return Variable
572
     * @throws InvalidParameterException
573
     * @throws NotFoundException
574
     * @throws ConflictException
575
     * @deprecated
576
     */
577
    public function setVariable($channelId, $variable, $value)
578
    {
579
        return $this->setChannelVar($channelId, $variable, $value);
580
    }
581
582
    /**
583
     * Start snooping. Snoop (spy/whisper) on a specific channel.
584
     *
585
     * @param string $channelId Channel's id
586
     * @param string $spy (default none) Direction of audio to spy on
587
     * @param string $whisper (default none) Direction of audio to whisper into
588
     * @param string $app (required) Application the snooping channel is placed into
589
     * @param string $appArgs The application arguments to pass to the Stasis application
590
     * @param string $snoopId Unique ID to assign to snooping channel
591
     * @return Channel
592
     * @throws InvalidParameterException
593
     * @throws NotFoundException
594
     */
595 View Code Duplication
    public function startSnoop($channelId, $spy, $whisper, $app, $appArgs, $snoopId)
596
    {
597
        $uri = "channels/$channelId/snoop";
598
        try {
599
            $response = $this->client->getEndpoint()->post($uri, [
600
                'form_params' => [
601
                    'spy' => $spy,
602
                    'whisper' => $whisper,
603
                    'app' => $app,
604
                    'appArgs' => $appArgs,
605
                    'snoopId' => $snoopId,
606
                ]
607
            ]);
608
        } catch (RequestException $e) {
609
            $this->processRequestException($e);
610
        }
611
612
        return new Channel($this->client, \GuzzleHttp\json_decode($response->getBody()));
613
    }
614
615
    /**
616
     * Start snooping. Snoop (spy/whisper) on a specific channel.
617
     *
618
     * @param string $channelId Channel's id
619
     * @param string $spy (default none) Direction of audio to spy on
620
     * @param string $whisper (default none) Direction of audio to whisper into
621
     * @param string $app (required) Application the snooping channel is placed into
622
     * @param string $appArgs The application arguments to pass to the Stasis application
623
     * @param string $snoopId Unique ID to assign to snooping channel
624
     * @return Channel
625
     * @throws InvalidParameterException
626
     * @throws NotFoundException
627
     */
628 View Code Duplication
    public function startSnoopWithId($channelId, $spy, $whisper, $app, $appArgs, $snoopId)
629
    {
630
        $uri = "channels/$channelId/snoop/$snoopId";
631
        try {
632
            $response = $this->client->getEndpoint()->post($uri, [
633
                'form_params' => [
634
                    'spy' => $spy,
635
                    'whisper' => $whisper,
636
                    'app' => $app,
637
                    'appArgs' => $appArgs,
638
                ]
639
            ]);
640
        } catch (RequestException $e) {
641
            $this->processRequestException($e);
642
        }
643
644
        return new Channel($this->client, \GuzzleHttp\json_decode($response->getBody()));
645
    }
646
647
    /**
648
     * @return string
649
     */
650
    public function getType()
651
    {
652
        return 'channels';
653
    }
654
}
655