Connection::onRead()   F
last analyzed

Complexity

Conditions 23
Paths 75

Size

Total Lines 114

Duplication

Lines 28
Ratio 24.56 %

Importance

Changes 0
Metric Value
cc 23
dl 28
loc 114
rs 3.3333
c 0
b 0
f 0
nc 75
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace PHPDaemon\Clients\Asterisk;
3
4
use PHPDaemon\Core\CallbackWrapper;
5
use PHPDaemon\Core\Daemon;
6
use PHPDaemon\Network\ClientConnection;
7
use PHPDaemon\Structures\StackCallbacks;
8
9
/**
10
 * Asterisk Call Manager Connection
11
 */
12
class Connection extends ClientConnection
13
{
14
    /**
15
     * @TODO DESCR
16
     */
17
    const CONN_STATE_START = 0;
18
19
    /**
20
     * @TODO DESCR
21
     */
22
    const CONN_STATE_GOT_INITIAL_PACKET = 0.1;
23
24
    /**
25
     * @TODO DESCR
26
     */
27
    const CONN_STATE_AUTH = 1;
28
29
    /**
30
     * @TODO DESCR
31
     */
32
    const CONN_STATE_LOGIN_PACKET_SENT = 1.1;
33
34
    /**
35
     * @TODO DESCR
36
     */
37
    const CONN_STATE_CHALLENGE_PACKET_SENT = 1.2;
38
39
    /**
40
     * @TODO DESCR
41
     */
42
    const CONN_STATE_LOGIN_PACKET_SENT_AFTER_CHALLENGE = 1.3;
43
44
    /**
45
     * @TODO DESCR
46
     */
47
    const CONN_STATE_HANDSHAKED_OK = 2.1;
48
49
    /**
50
     * @TODO DESCR
51
     */
52
    const CONN_STATE_HANDSHAKED_ERROR = 2.2;
53
54
    /**
55
     * @TODO DESCR
56
     */
57
    const INPUT_STATE_START = 0;
58
59
    /**
60
     * @TODO DESCR
61
     */
62
    const INPUT_STATE_END_OF_PACKET = 1;
63
64
    /**
65
     * @TODO DESCR
66
     */
67
    const INPUT_STATE_PROCESSING = 2;
68
69
    /**
70
     * @var string EOL
71
     */
72
    public $EOL = "\r\n";
73
74
    /**
75
     * @var string The username to access the interface
76
     */
77
    public $username;
78
79
    /**
80
     * @var string The password defined in manager interface of server
81
     */
82
    public $secret;
83
84
    /**
85
     * @var float Connection's state
86
     */
87
    public $state = self::CONN_STATE_START;
88
89
    /**
90
     * @var integer Input state
91
     */
92
    public $instate = self::INPUT_STATE_START;
93
94
    /**
95
     * @var array Received packets
96
     */
97
    public $packets = [];
98
99
    /**
100
     * @var integer For composite response on action
101
     */
102
    public $cnt = 0;
103
104
    /**
105
     * @var array Stack of callbacks called when response received
106
     */
107
    public $callbacks = [];
108
109
    /**
110
     * Assertions for callbacks.
111
     * Assertion: if more events may follow as response this is a main part or full
112
     * an action complete event indicating that all data has been sent
113
     * @var array
114
     */
115
    public $assertions = [];
116
117
    /**
118
     * @var callable Callback. Called when received response on challenge action
119
     */
120
    public $onChallenge;
121
122
    /**
123
     * Execute the given callback when/if the connection is handshaked
124
     * @param  callable Callback
125
     * @return void
126
     */
127 View Code Duplication
    public function onConnected($cb)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
128
    {
129
        if ($this->state === self::CONN_STATE_HANDSHAKED_ERROR) {
130
            $cb($this);
131
        } elseif ($this->state === self::CONN_STATE_HANDSHAKED_OK) {
132
            $cb($this);
133
        } else {
134
            if (!$this->onConnected) {
135
                $this->onConnected = new StackCallbacks();
136
            }
137
138
            $this->onConnected->push($cb);
139
        }
140
    }
141
142
    /**
143
     * Called when the connection is handshaked (at low-level), and peer is ready to recv. data
144
     * @return void
145
     */
146
    public function onReady()
147
    {
148
        if ($this->url === null) {
149
            return;
150
        }
151
152
        if ($this->connected && !$this->busy) {
153
            $this->pool->servConnFree[$this->url]->attach($this);
154
        }
155
156
        $url = parse_url($this->url);
157
158
        $this->username = $url['user'];
159
        $this->secret = $url['pass'];
160
    }
161
162
    /**
163
     * Called when the worker is going to shutdown
164
     * @return boolean Ready to shutdown?
165
     */
166
    public function gracefulShutdown()
167
    {
168
        if ($this->finished) {
169
            return !$this->writing;
170
        }
171
172
        $this->logoff();
173
174
        $this->finish();
175
176
        return false;
177
    }
178
179
    /**
180
     * Called when session finishes
181
     * @return void
182
     */
183
    public function onFinish()
184
    {
185
        $this->state = self::CONN_STATE_START;
0 ignored issues
show
Documentation Bug introduced by
The property $state was declared of type double, but self::CONN_STATE_START is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
186
187
        parent::onFinish();
188
189
        $this->event('disconnect');
190
    }
191
192
    /**
193
     * Called when new data received
194
     * @return void
195
     */
196
    public function onRead()
197
    {
198
        if ($this->state === self::CONN_STATE_START) {
199
            if (($ver = $this->readline()) === null) {
200
                return;
201
            }
202
            $this->pool->setAmiVersion($this->addr, $ver);
203
            $this->state = self::CONN_STATE_GOT_INITIAL_PACKET;
204
            $this->auth();
205
        }
206
207
        while (($line = $this->readline()) !== null) {
208
            //Daemon::log('>>> '.$line);
209
            if ($line === '') {
210
                $this->instate = self::INPUT_STATE_END_OF_PACKET;
211
                $packet =& $this->packets[$this->cnt];
212
                ++$this->cnt;
213
            } else {
214
                $this->instate = self::INPUT_STATE_PROCESSING;
215
                list($header, $value) = Pool::extract($line);
216
                $this->packets[$this->cnt][$header] = $value;
217
            }
218
219
            if ((int)$this->state === self::CONN_STATE_AUTH) {
220
                if ($this->instate === self::INPUT_STATE_END_OF_PACKET) {
221
                    if ($packet['response'] === 'success') {
222
                        if ($this->state === self::CONN_STATE_CHALLENGE_PACKET_SENT) {
223
                            if (is_callable($this->onChallenge)) {
224
                                $func = $this->onChallenge;
225
                                $func($this, $packet['challenge']);
0 ignored issues
show
Bug introduced by
The variable $packet does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
226
                            }
227 View Code Duplication
                        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
228
                            if ($packet['message'] === 'authentication accepted') {
229
                                $this->state = self::CONN_STATE_HANDSHAKED_OK;
230
231
                                Daemon::$process->log(
232
                                    __METHOD__ . ': Authentication ok. Connected to ' .
233
                                    parse_url($this->addr, PHP_URL_HOST)
234
                                );
235
236
                                if ($this->onConnected) {
237
                                    $this->connected = true;
238
                                    $this->onConnected->executeAll($this);
239
                                    $this->onConnected = null;
240
                                }
241
242
                                $this->event('connected');
243
                            }
244
                        }
245 View Code Duplication
                    } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
246
                        $this->state = self::CONN_STATE_HANDSHAKED_ERROR;
247
248
                        Daemon::$process->log(
249
                            __METHOD__ . ': Authentication failed. Connection to ' .
250
                            parse_url($this->addr, PHP_URL_HOST) . ' failed.'
251
                        );
252
253
                        if ($this->onConnected) {
254
                            $this->connected = false;
255
                            $this->onConnected->executeAll($this);
256
                            $this->onConnected = null;
257
                        }
258
259
                        $this->finish();
260
                    }
261
262
                    $this->packets = [];
263
                }
264
            } elseif ($this->state === self::CONN_STATE_HANDSHAKED_OK) {
265
                if ($this->instate === self::INPUT_STATE_END_OF_PACKET) {
266
                    // Event
267
                    if (isset($packet['event']) && !isset($packet['actionid'])) {
268
                        $this->event('event_' . $packet['event'], $packet);
269
                        $this->event('event', $packet);
270
                    } // Response
271
                    elseif (isset($packet['actionid'])) {
272
                        $action_id =& $packet['actionid'];
273
274
                        if (isset($this->callbacks[$action_id])) {
275
                            if (isset($this->assertions[$action_id])) {
276
                                $this->packets[$action_id][] = $packet;
277
278
                                $assertations = count(
279
                                    array_uintersect_uassoc(
280
                                        $this->assertions[$action_id],
281
                                        $packet,
282
                                        'strcasecmp',
283
                                        'strcasecmp'
284
                                    )
285
                                );
286
                                if ($assertations === count($this->assertions[$action_id])) {
287
                                    if (is_callable($this->callbacks[$action_id])) {
288
                                        $this->callbacks[$action_id]($this, $this->packets[$action_id]);
289
                                        unset($this->callbacks[$action_id]);
290
                                    }
291
292
                                    unset($this->assertions[$action_id]);
293
                                    unset($this->packets[$action_id]);
294
                                }
295
                            } else {
296
                                if (is_callable($this->callbacks[$action_id])) {
297
                                    $this->callbacks[$action_id]($this, $packet);
298
                                    unset($this->callbacks[$action_id]);
299
                                }
300
                            }
301
                        }
302
                    }
303
304
                    unset($packet);
305
                    unset($this->packets[$this->cnt - 1]);
306
                }
307
            }
308
        }
309
    }
310
311
    /**
312
     * Send authentication packet
313
     * @return void
314
     */
315
    protected function auth()
316
    {
317
        if ($this->state !== self::CONN_STATE_GOT_INITIAL_PACKET) {
318
            return;
319
        }
320
321
        if ($this->pool->config->authtype->value === 'md5') {
322
            $this->challenge(function ($conn, $challenge) {
323
                $packet = "Action: Login\r\n"
324
                    . "AuthType: MD5\r\n"
325
                    . "Username: " . $this->username . "\r\n"
326
                    . "Key: " . md5($challenge . $this->secret) . "\r\n"
327
                    . "Events: on\r\n"
328
                    . "\r\n";
329
                $this->state = self::CONN_STATE_LOGIN_PACKET_SENT_AFTER_CHALLENGE;
330
                $conn->write($packet);
331
            });
332
        } else {
333
            $this->login();
334
        }
335
    }
336
337
    /**
338
     * Action: Login
339
     * Synopsis: Login Manager
340
     * Privilege: <none>
341
     *
342
     * @return void
343
     */
344
    protected function login()
345
    {
346
        $this->state = self::CONN_STATE_LOGIN_PACKET_SENT;
347
        $this->write(
348
            "Action: login\r\n"
349
            . "Username: " . $this->username . "\r\n"
350
            . "Secret: " . $this->secret . "\r\n"
351
            . "Events: on\r\n"
352
            . "\r\n"
353
        );
354
    }
355
356
    /**
357
     * Action: Challenge
358
     * Synopsis: Generate Challenge for MD5 Auth
359
     * Privilege: <none>
360
     *
361
     * @param  callable $cb
362
     * @return void
363
     */
364
    protected function challenge($cb)
365
    {
366
        $this->onChallenge = $cb;
367
        $this->state = self::CONN_STATE_CHALLENGE_PACKET_SENT;
368
        $this->write(
369
            "Action: Challenge\r\n"
370
            . "AuthType: MD5\r\n"
371
            . "\r\n"
372
        );
373
    }
374
375
    /**
376
     * Action: SIPpeers
377
     * Synopsis: List SIP peers (text format)
378
     * Privilege: system,reporting,all
379
     * Description: Lists SIP peers in text format with details on current status.
380
     * Peerlist will follow as separate events, followed by a final event called
381
     * PeerlistComplete.
382
     * Variables:
383
     * ActionID: <id>    Action ID for this transaction. Will be returned.
384
     *
385
     * @param callable $cb Callback called when response received
386
     * @callback $cb ( Connection $conn, array $packet )
387
     * @return void
388
     */
389
    public function getSipPeers($cb)
390
    {
391
        $this->command("Action: SIPpeers\r\n", $cb, ['event' => 'peerlistcomplete']);
392
    }
393
394
    /**
395
     * Action: IAXpeerlist
396
     * Synopsis: List IAX Peers
397
     * Privilege: system,reporting,all
398
     *
399
     * @param callable $cb Callback called when response received
400
     * @callback $cb ( Connection $conn, array $packet )
401
     * @return void
402
     */
403
    public function getIaxPeers($cb)
404
    {
405
        $this->command("Action: IAXpeerlist\r\n", $cb, ['event' => 'peerlistcomplete']);
406
    }
407
408
    /**
409
     * Action: GetConfig
410
     * Synopsis: Retrieve configuration
411
     * Privilege: system,config,all
412
     * Description: A 'GetConfig' action will dump the contents of a configuration
413
     * file by category and contents or optionally by specified category only.
414
     * Variables: (Names marked with * are required)
415
     *   *Filename: Configuration filename (e.g. foo.conf)
416
     *   Category: Category in configuration file
417
     *
418
     * @param  string $filename Filename
419
     * @param  callable $cb Callback called when response received
420
     * @callback $cb ( Connection $conn, array $packet )
421
     * @return void
422
     */
423
    public function getConfig($filename, $cb)
424
    {
425
        $this->command("Action: GetConfig\r\nFilename: " . trim($filename) . "\r\n", $cb);
426
    }
427
428
    /**
429
     * Action: GetConfigJSON
430
     * Synopsis: Retrieve configuration
431
     * Privilege: system,config,all
432
     * Description: A 'GetConfigJSON' action will dump the contents of a configuration
433
     * file by category and contents in JSON format.  This only makes sense to be used
434
     * using rawman over the HTTP interface.
435
     * Variables:
436
     *    Filename: Configuration filename (e.g. foo.conf)
437
     *
438
     * @param  string $filename Filename
439
     * @param callable $cb Callback called when response received
440
     * @callback $cb ( Connection $conn, array $packet )
441
     * @return void
442
     */
443
    public function getConfigJSON($filename, $cb)
444
    {
445
        $this->command("Action: GetConfigJSON\r\nFilename: " . trim($filename) . "\r\n", $cb);
446
    }
447
448
    /**
449
     * Action: Setvar
450
     * Synopsis: Set Channel Variable
451
     * Privilege: call,all
452
     * Description: Set a global or local channel variable.
453
     * Variables: (Names marked with * are required)
454
     * Channel: Channel to set variable for
455
     *  *Variable: Variable name
456
     *  *Value: Value
457
     *
458
     * @param string $channel
459
     * @param string $variable
460
     * @param string $value
461
     * @param callable $cb
462
     * @callback $cb ( Connection $conn, array $packet )
463
     * @return void
464
     */
465
    public function setVar($channel, $variable, $value, $cb)
466
    {
467
        $cmd = "Action: SetVar\r\n";
468
469
        if ($channel) {
470
            $cmd .= "Channel: " . trim($channel) . "\r\n";
471
        }
472
473
        if (isset($variable, $value)) {
474
            $cmd .= "Variable: " . trim($variable) . "\r\n"
475
                . "Value: " . trim($value) . "\r\n";
476
477
            $this->command($cmd, $cb);
478
        }
479
    }
480
481
    /**
482
     * Action: CoreShowChannels
483
     * Synopsis: List currently active channels
484
     * Privilege: system,reporting,all
485
     * Description: List currently defined channels and some information
486
     *        about them.
487
     * Variables:
488
     *        ActionID: Optional Action id for message matching.
489
     *
490
     * @param callable $cb
491
     * @callback $cb ( Connection $conn, array $packet )
492
     * @return void
493
     */
494
    public function coreShowChannels($cb)
495
    {
496
        $this->command(
497
            "Action: CoreShowChannels\r\n",
498
            $cb,
499
            ['event' => 'coreshowchannelscomplete', 'eventlist' => 'complete']
500
        );
501
    }
502
503
    /**
504
     * Action: Status
505
     * Synopsis: Lists channel status
506
     * Privilege: system,call,reporting,all
507
     * Description: Lists channel status along with requested channel vars.
508
     * Variables: (Names marked with * are required)
509
     *Channel: Name of the channel to query for status
510
     *    Variables: Comma ',' separated list of variables to include
511
     * ActionID: Optional ID for this transaction
512
     * Will return the status information of each channel along with the
513
     * value for the specified channel variables.
514
     *
515
     * @param  callable $cb
516
     * @param  string $channel
0 ignored issues
show
Documentation introduced by
Should the type for parameter $channel not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
517
     * @callback $cb ( Connection $conn, array $packet )
518
     * @return void
519
     */
520
    public function status($cb, $channel = null)
521
    {
522
        $cmd = "Action: Status\r\n";
523
524
        if ($channel !== null) {
525
            $cmd .= 'Channel: ' . trim($channel) . "\r\n";
526
        }
527
528
        $this->command($cmd, $cb, ['event' => 'statuscomplete']);
529
    }
530
531
    /**
532
     * Action: Redirect
533
     * Synopsis: Redirect (transfer) a call
534
     * Privilege: call,all
535
     * Description: Redirect (transfer) a call.
536
     * Variables: (Names marked with * are required)
537
     * *Channel: Channel to redirect
538
     *  ExtraChannel: Second call leg to transfer (optional)
539
     * *Exten: Extension to transfer to
540
     * *Context: Context to transfer to
541
     * *Priority: Priority to transfer to
542
     * ActionID: Optional Action id for message matching.
543
     *
544
     * @param array $params
545
     * @param callable $cb Callback called when response received
546
     * @callback $cb ( Connection $conn, array $packet )
547
     * @return void
548
     */
549
    public function redirect(array $params, $cb)
550
    {
551
        $this->command("Action: Redirect\r\n" . $this->implodeParams($params), $cb);
552
    }
553
554
    /**
555
     * Action: Originate
556
     * Synopsis: Originate a call
557
     * Privilege: call,all
558
     * Description: first the Channel is rung. Then, when that answers, the Extension is dialled within the Context
559
     *  to initiate the other end of the call.
560
     * Variables: (Names marked with * are required)
561
     * *Channel: Channel on which to originate the call (The same as you specify in the Dial application command)
562
     * *Context: Context to use on connect (must use Exten & Priority with it)
563
     * *Exten: Extension to use on connect (must use Context & Priority with it)
564
     * *Priority: Priority to use on connect (must use Context & Exten with it)
565
     * Timeout: Timeout (in milliseconds) for the originating connection to happen(defaults to 30000 milliseconds)
566
     * *CallerID: CallerID to use for the call
567
     * Variable: Channels variables to set (max 32). Variables will be set for both channels (local and connected).
568
     * Account: Account code for the call
569
     * Application: Application to use on connect (use Data for parameters)
570
     * Data : Data if Application parameter is used
571
     * ActionID: Optional Action id for message matching.
572
     *
573
     * @param array $params
574
     * @param callable $cb Callback called when response received
575
     * @callback $cb ( Connection $conn, array $packet )
576
     * @return void
577
     */
578
    public function originate(array $params, $cb)
579
    {
580
        $params['Async'] = 1;
581
582
        $this->command("Action: Originate\r\n" . $this->implodeParams($params), $cb);
583
    }
584
585
    /**
586
     * Action: ExtensionState
587
     * Synopsis: Get an extension's state.
588
     * Description: function can be used to retrieve the state from any    hinted extension.
589
     * Variables: (Names marked with * are required)
590
     * *Exten: Extension to get state
591
     * Context: Context for exten
592
     * ActionID: Optional Action id for message matching.
593
     *
594
     * @param array $params
595
     * @param callable $cb Callback called when response received
596
     * @callback $cb ( Connection $conn, array $packet )
597
     * @return void
598
     */
599
    public function extensionState(array $params, $cb)
600
    {
601
        $this->command("Action: ExtensionState\r\n" . $this->implodeParams($params), $cb);
602
    }
603
604
    /**
605
     * Action: Ping
606
     * Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the
607
     *   manager connection open.
608
     * Variables: NONE
609
     *
610
     * @param callable $cb Callback called when response received
611
     * @callback $cb ( Connection $conn, array $packet )
612
     * @return void
613
     */
614
    public function ping($cb)
615
    {
616
        $this->command("Action: Ping\r\n", $cb);
617
    }
618
619
    /**
620
     * For almost any actions in Action: ListCommands
621
     * Privilege: depends on $action
622
     *
623
     * @param string $action Action
624
     * @param callable $cb Callback called when response received
625
     * @param array $params Parameters
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be null|array?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
626
     * @param array $assertion If more events may follow as response this is a main part or full an action complete event indicating that all data has been sent
0 ignored issues
show
Documentation introduced by
Should the type for parameter $assertion not be null|array?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
627
     * @callback $cb ( Connection $conn, array $packet )
628
     * @return void
629
     */
630
    public function action($action, $cb, array $params = null, array $assertion = null)
631
    {
632
        $action = trim($action);
633
634
        $this->command("Action: {$action}\r\n" . ($params ? $this->implodeParams($params) : ''), $cb, $assertion);
635
    }
636
637
    /**
638
     * Action: Logoff
639
     * Synopsis: Logoff Manager
640
     * Privilege: <none>
641
     * Description: Logoff this manager session
642
     * Variables: NONE
643
     *
644
     * @param callable $cb Optional callback called when response received
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
645
     * @callback $cb ( Connection $conn, array $packet )
646
     * @return void
647
     */
648
    public function logoff($cb = null)
649
    {
650
        $this->command("Action: Logoff\r\n", $cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by parameter $cb on line 648 can also be of type null; however, PHPDaemon\Clients\Asterisk\Connection::command() does only seem to accept callable, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
651
    }
652
653
    /**
654
     * Called when event occured
655
     * @deprecated Replaced with ->bind('event', ...)
656
     * @param callable $cb Callback
657
     * @return void
658
     */
659
    public function onEvent($cb)
660
    {
661
        $this->bind('event', $cb);
662
    }
663
664
    /**
665
     * Sends arbitrary command
666
     * @param string $packet A packet for sending by the connected client to Asterisk
667
     * @param callable $cb Callback called when response received
668
     * @param array $assertion If more events may follow as response this is a main part or full an action complete event indicating that all data has been sent
0 ignored issues
show
Documentation introduced by
Should the type for parameter $assertion not be array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
669
     */
670
    protected function command($packet, $cb, $assertion = null)
671
    {
672
        if ($this->finished) {
673
            throw new ConnectionFinished;
674
        }
675
676
        if ($this->state !== self::CONN_STATE_HANDSHAKED_OK) {
677
            return;
678
        }
679
680
        $actionId = Daemon::uniqid();
681
682
        if (!is_callable($cb, true)) {
683
            $cb = false;
684
        }
685
686
        $this->callbacks[$actionId] = CallbackWrapper::wrap($cb);
0 ignored issues
show
Security Bug introduced by
It seems like $cb defined by false on line 683 can also be of type false; however, PHPDaemon\Core\CallbackWrapper::wrap() does only seem to accept callable, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
687
688
        if ($assertion !== null) {
689
            $this->assertions[$actionId] = $assertion;
690
        }
691
692
        $this->write($packet);
693
        $this->write('ActionID: ' . $actionId . "\r\n\r\n");
694
    }
695
696
    /**
697
     * Generate AMI packet string from associative array provided
698
     * @param  array $params
699
     * @return string
700
     */
701
    protected function implodeParams(array $params)
702
    {
703
        $s = '';
704
705
        foreach ($params as $header => $value) {
706
            $s .= trim($header) . ": " . trim($value) . "\r\n";
707
        }
708
709
        return $s;
710
    }
711
}
712