Completed
Push — master ( a4fa7b...83f79a )
by Vasily
05:57
created

Connection::onRead()   D

Complexity

Conditions 23
Paths 75

Size

Total Lines 105
Code Lines 63

Duplication

Lines 28
Ratio 26.67 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 28
loc 105
rs 4.6303
cc 23
eloc 63
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\Clients\Asterisk\ConnectionFinished;
5
use PHPDaemon\Clients\Asterisk\Pool;
6
use PHPDaemon\Core\CallbackWrapper;
7
use PHPDaemon\Core\Daemon;
8
use PHPDaemon\Network\ClientConnection;
9
use PHPDaemon\Structures\StackCallbacks;
10
use PHPDaemon\Traits\EventHandlers;
11
12
/**
13
 * Asterisk Call Manager Connection
14
 */
15
class Connection extends ClientConnection {
16
17
	/**
18
	 * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
19
	 */
20
	const CONN_STATE_START                             = 0;
21
22
	/**
23
	 * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
24
	 */
25
	const CONN_STATE_GOT_INITIAL_PACKET                = 0.1;
26
27
	/**
28
	 * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
29
	 */
30
	const CONN_STATE_AUTH                              = 1;
31
32
	/**
33
	 * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
34
	 */
35
	const CONN_STATE_LOGIN_PACKET_SENT                 = 1.1;
36
37
	/**
38
	 * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
39
	 */
40
	const CONN_STATE_CHALLENGE_PACKET_SENT             = 1.2;
41
42
	/**
43
	 * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
44
	 */
45
	const CONN_STATE_LOGIN_PACKET_SENT_AFTER_CHALLENGE = 1.3;
46
47
	/**
48
	 * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
49
	 */
50
	const CONN_STATE_HANDSHAKED_OK                     = 2.1;
51
52
	/**
53
	 * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
54
	 */
55
	const CONN_STATE_HANDSHAKED_ERROR                  = 2.2;
56
57
	/**
58
	 * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
59
	 */
60
	const INPUT_STATE_START                            = 0;
61
62
	/**
63
	 * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
64
	 */
65
	const INPUT_STATE_END_OF_PACKET                    = 1;
66
67
	/**
68
	 * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
69
	 */
70
	const INPUT_STATE_PROCESSING                       = 2;
71
72
	/**
73
	 * @var string EOL
74
	 */
75
	public $EOL = "\r\n";
76
77
	/**
78
	 * @var string The username to access the interface
79
	 */
80
	public $username;
81
82
	/**
83
	 * @var string The password defined in manager interface of server
84
	 */
85
	public $secret;
86
87
	/**
88
	 * @var float Connection's state
89
	 */
90
	public $state = self::CONN_STATE_START;
91
92
	/**
93
	 * @var integer Input state
94
	 */
95
	public $instate = self::INPUT_STATE_START;
96
97
	/**
98
	 * @var array Received packets
99
	 */
100
	public $packets = [];
101
102
	/**
103
	 * @var integer For composite response on action
104
	 */
105
	public $cnt = 0;
106
107
	/**
108
	 * @var array Stack of callbacks called when response received
109
	 */
110
	public $callbacks = [];
111
112
	/**
113
	 * Assertions for callbacks.
114
	 * Assertion: if more events may follow as response this is a main part or full
115
	 * an action complete event indicating that all data has been sent
116
	 * @var array
117
	 */
118
	public $assertions = [];
119
120
	/**
121
	 * @var callable Callback. Called when received response on challenge action
122
	 */
123
	public $onChallenge;
124
125
	/**
126
	 * Execute the given callback when/if the connection is handshaked
127
	 * @param  callable Callback
128
	 * @return void
129
	 */
130
	public function onConnected($cb) {
131 View Code Duplication
		if ($this->state === self::CONN_STATE_HANDSHAKED_ERROR) {
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...
132
			$cb($this);
133
		}
134
		elseif ($this->state === self::CONN_STATE_HANDSHAKED_OK) {
135
			$cb($this);
136
		}
137
		else {
138
			if (!$this->onConnected) {
139
				$this->onConnected = new StackCallbacks();
140
			}
141
142
			$this->onConnected->push($cb);
143
		}
144
	}
145
146
	/**
147
	 * Called when the connection is handshaked (at low-level), and peer is ready to recv. data
148
	 * @return void
149
	 */
150
	public function onReady() {
151
		if ($this->url === null) {
152
			return;
153
		}
154
155
		if ($this->connected && !$this->busy) {
156
			$this->pool->servConnFree[$this->url]->attach($this);
157
		}
158
159
		$url = parse_url($this->url);
160
161
		$this->username = $url['user'];
162
		$this->secret   = $url['pass'];
163
	}
164
165
	/**
166
	 * Called when the worker is going to shutdown
167
	 * @return boolean Ready to shutdown?
168
	 */
169
	public function gracefulShutdown() {
170
		if ($this->finished) {
171
			return !$this->writing;
172
		}
173
174
		$this->logoff();
175
176
		$this->finish();
177
178
		return false;
179
	}
180
181
	/**
182
	 * Called when session finishes
183
	 * @return void
184
	 */
185
	public function onFinish() {
186
		$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...
187
188
		parent::onFinish();
189
190
		$this->event('disconnect');
191
	}
192
193
	/**
194
	 * Called when new data received
195
	 * @return void
196
	 */
197
	public function onRead() {
198
199
		if ($this->state === self::CONN_STATE_START) {
200
			if (($ver = $this->readline()) === null) {
201
				return;
202
			}
203
			$this->pool->setAmiVersion($this->addr, $ver);
204
			$this->state = self::CONN_STATE_GOT_INITIAL_PACKET;
205
			$this->auth();
206
		}
207
208
		while (($line = $this->readline()) !== null) {
209
			//Daemon::log('>>> '.$line);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
210
			if ($line === '') {
211
				$this->instate = self::INPUT_STATE_END_OF_PACKET;
212
				$packet        =& $this->packets[$this->cnt];
213
				++$this->cnt;
214
			}
215
			else {
216
				$this->instate = self::INPUT_STATE_PROCESSING;
217
				list($header, $value) = Pool::extract($line);
218
				$this->packets[$this->cnt][$header] = $value;
219
			}
220
221
			if ((int)$this->state === self::CONN_STATE_AUTH) {
222
				if ($this->instate === self::INPUT_STATE_END_OF_PACKET) {
223
					if ($packet['response'] === 'success') {
224
						if ($this->state === self::CONN_STATE_CHALLENGE_PACKET_SENT) {
225
							if (is_callable($this->onChallenge)) {
226
								$func = $this->onChallenge;
227
								$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...
228
							}
229
						}
230 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...
231
							if ($packet['message'] === 'authentication accepted') {
232
								$this->state = self::CONN_STATE_HANDSHAKED_OK;
233
234
								Daemon::$process->log(__METHOD__ . ': Authentication ok. Connected to ' . parse_url($this->addr, PHP_URL_HOST));
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
					}
246 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...
247
						$this->state = self::CONN_STATE_HANDSHAKED_ERROR;
248
249
						Daemon::$process->log(__METHOD__ . ': Authentication failed. Connection to ' . parse_url($this->addr, PHP_URL_HOST) . ' failed.');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 136 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
250
251
						if ($this->onConnected) {
252
							$this->connected = false;
253
							$this->onConnected->executeAll($this);
254
							$this->onConnected = null;
255
						}
256
257
						$this->finish();
258
					}
259
260
					$this->packets = [];
261
				}
262
			}
263
			elseif ($this->state === self::CONN_STATE_HANDSHAKED_OK) {
264
				if ($this->instate === self::INPUT_STATE_END_OF_PACKET) {
265
					// Event
266
					if (isset($packet['event']) && !isset($packet['actionid'])) {
267
						$this->event('event_' . $packet['event'], $packet);
268
						$this->event('event', $packet);
269
					}
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
								if (count(array_uintersect_uassoc($this->assertions[$action_id], $packet, 'strcasecmp', 'strcasecmp')) === count($this->assertions[$action_id])) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 154 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
278
									if (is_callable($this->callbacks[$action_id])) {
279
										$this->callbacks[$action_id]($this, $this->packets[$action_id]);
280
										unset($this->callbacks[$action_id]);
281
									}
282
283
									unset($this->assertions[$action_id]);
284
									unset($this->packets[$action_id]);
285
								}
286
							}
287
							else {
288
								if (is_callable($this->callbacks[$action_id])) {
289
									$this->callbacks[$action_id]($this, $packet);
290
									unset($this->callbacks[$action_id]);
291
								}
292
							}
293
						}
294
					}
295
296
					unset($packet);
297
					unset($this->packets[$this->cnt - 1]);
298
				}
299
			}
300
		}
301
	}
302
303
	/**
304
	 * Send authentication packet
305
	 * @return void
306
	 */
307
	protected function auth() {
308
		if ($this->state !== self::CONN_STATE_GOT_INITIAL_PACKET) {
309
			return;
310
		}
311
312
		if ($this->pool->config->authtype->value === 'md5') {
313
			$this->challenge(function ($conn, $challenge) {
314
				$packet      = "Action: Login\r\n"
315
						. "AuthType: MD5\r\n"
316
						. "Username: " . $this->username . "\r\n"
317
						. "Key: " . md5($challenge . $this->secret) . "\r\n"
318
						. "Events: on\r\n"
319
						. "\r\n";
320
				$this->state = self::CONN_STATE_LOGIN_PACKET_SENT_AFTER_CHALLENGE;
321
				$conn->write($packet);
322
			});
323
		}
324
		else {
325
			$this->login();
326
		}
327
	}
328
329
	/**
330
	 * Action: Login
331
	 * Synopsis: Login Manager
332
	 * Privilege: <none>
333
	 *
334
	 * @return void
335
	 */
336
	protected function login() {
337
		$this->state = self::CONN_STATE_LOGIN_PACKET_SENT;
338
		$this->write(
339
			"Action: login\r\n"
340
			. "Username: " . $this->username . "\r\n"
341
			. "Secret: " . $this->secret . "\r\n"
342
			. "Events: on\r\n"
343
			. "\r\n"
344
		);
345
	}
346
347
	/**
348
	 * Action: Challenge
349
	 * Synopsis: Generate Challenge for MD5 Auth
350
	 * Privilege: <none>
351
	 *
352
	 * @param  callable $cb
353
	 * @return void
354
	 */
355
	protected function challenge($cb) {
356
		$this->onChallenge = $cb;
357
		$this->state       = self::CONN_STATE_CHALLENGE_PACKET_SENT;
358
		$this->write(
359
			"Action: Challenge\r\n"
360
			. "AuthType: MD5\r\n"
361
			. "\r\n");
362
	}
363
364
	/**
365
	 * Action: SIPpeers
366
	 * Synopsis: List SIP peers (text format)
367
	 * Privilege: system,reporting,all
368
	 * Description: Lists SIP peers in text format with details on current status.
369
	 * Peerlist will follow as separate events, followed by a final event called
370
	 * PeerlistComplete.
371
	 * Variables:
372
	 * ActionID: <id>    Action ID for this transaction. Will be returned.
373
	 *
374
	 * @param callable $cb Callback called when response received
375
	 * @callback $cb ( Connection $conn, array $packet )
376
	 * @return void
377
	 */
378
	public function getSipPeers($cb) {
379
		$this->command("Action: SIPpeers\r\n", $cb, ['event' => 'peerlistcomplete']);
380
	}
381
382
	/**
383
	 * Action: IAXpeerlist
384
	 * Synopsis: List IAX Peers
385
	 * Privilege: system,reporting,all
386
	 *
387
	 * @param callable $cb Callback called when response received
388
	 * @callback $cb ( Connection $conn, array $packet )
389
	 * @return void
390
	 */
391
	public function getIaxPeers($cb) {
392
		$this->command("Action: IAXpeerlist\r\n", $cb, ['event' => 'peerlistcomplete']);
393
	}
394
395
	/**
396
	 * Action: GetConfig
397
	 * Synopsis: Retrieve configuration
398
	 * Privilege: system,config,all
399
	 * Description: A 'GetConfig' action will dump the contents of a configuration
400
	 * file by category and contents or optionally by specified category only.
401
	 * Variables: (Names marked with * are required)
402
	 *   *Filename: Configuration filename (e.g. foo.conf)
403
	 *   Category: Category in configuration file
404
	 *
405
	 * @param  string   $filename Filename
406
	 * @param  callable $cb       Callback called when response received
407
	 * @callback $cb ( Connection $conn, array $packet )
408
	 * @return void
409
	 */
410
	public function getConfig($filename, $cb) {
411
		$this->command("Action: GetConfig\r\nFilename: " . trim($filename) . "\r\n", $cb);
412
	}
413
414
	/**
415
	 * Action: GetConfigJSON
416
	 * Synopsis: Retrieve configuration
417
	 * Privilege: system,config,all
418
	 * Description: A 'GetConfigJSON' action will dump the contents of a configuration
419
	 * file by category and contents in JSON format.  This only makes sense to be used
420
	 * using rawman over the HTTP interface.
421
	 * Variables:
422
	 *    Filename: Configuration filename (e.g. foo.conf)
423
	 *
424
	 * @param  string   $filename Filename
425
	 * @param callable $cb Callback called when response received
426
	 * @callback $cb ( Connection $conn, array $packet )
427
	 * @return void
428
	 */
429
	public function getConfigJSON($filename, $cb) {
430
		$this->command("Action: GetConfigJSON\r\nFilename: " . trim($filename) . "\r\n", $cb);
431
	}
432
433
	/**
434
	 * Action: Setvar
435
	 * Synopsis: Set Channel Variable
436
	 * Privilege: call,all
437
	 * Description: Set a global or local channel variable.
438
	 * Variables: (Names marked with * are required)
439
	 * Channel: Channel to set variable for
440
	 *  *Variable: Variable name
441
	 *  *Value: Value
442
	 * 
443
	 * @param string   $channel
444
	 * @param string   $variable
445
	 * @param string   $value
446
	 * @param callable $cb
447
	 * @callback $cb ( Connection $conn, array $packet )
448
	 * @return void
449
	 */
450
	public function setVar($channel, $variable, $value, $cb) {
451
		$cmd = "Action: SetVar\r\n";
452
453
		if ($channel) {
454
			$cmd .= "Channel: " . trim($channel) . "\r\n";
455
		}
456
457
		if (isset($variable, $value)) {
458
			$cmd .= "Variable: " . trim($variable) . "\r\n"
459
					. "Value: " . trim($value) . "\r\n";
460
461
			$this->command($cmd, $cb);
462
		}
463
	}
464
465
	/**
466
	 * Action: CoreShowChannels
467
	 * Synopsis: List currently active channels
468
	 * Privilege: system,reporting,all
469
	 * Description: List currently defined channels and some information
470
	 *        about them.
471
	 * Variables:
472
	 *        ActionID: Optional Action id for message matching.
473
	 *
474
	 * @param callable $cb
475
	 * @callback $cb ( Connection $conn, array $packet )
476
	 * @return void
477
	 */
478
	public function coreShowChannels($cb) {
479
		$this->command("Action: CoreShowChannels\r\n", $cb,
480
					   ['event' => 'coreshowchannelscomplete', 'eventlist' => 'complete']
481
		);
482
	}
483
484
	/**
485
	 * Action: Status
486
	 * Synopsis: Lists channel status
487
	 * Privilege: system,call,reporting,all
488
	 * Description: Lists channel status along with requested channel vars.
489
	 * Variables: (Names marked with * are required)
490
	 *Channel: Name of the channel to query for status
491
	 *    Variables: Comma ',' separated list of variables to include
492
	 * ActionID: Optional ID for this transaction
493
	 * Will return the status information of each channel along with the
494
	 * value for the specified channel variables.
495
	 *
496
	 * @param  callable $cb
497
	 * @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...
498
	 * @callback $cb ( Connection $conn, array $packet )
499
	 * @return void
500
	 */
501
	public function status($cb, $channel = null) {
502
		$cmd = "Action: Status\r\n";
503
504
		if ($channel !== null) {
505
			$cmd .= 'Channel: ' . trim($channel) . "\r\n";
506
		}
507
508
		$this->command($cmd, $cb, ['event' => 'statuscomplete']);
509
	}
510
511
	/**
512
	 * Action: Redirect
513
	 * Synopsis: Redirect (transfer) a call
514
	 * Privilege: call,all
515
	 * Description: Redirect (transfer) a call.
516
	 * Variables: (Names marked with * are required)
517
	 * *Channel: Channel to redirect
518
	 *  ExtraChannel: Second call leg to transfer (optional)
519
	 * *Exten: Extension to transfer to
520
	 * *Context: Context to transfer to
521
	 * *Priority: Priority to transfer to
522
	 * ActionID: Optional Action id for message matching.
523
	 *
524
	 * @param array    $params
525
	 * @param callable $cb     Callback called when response received
526
	 * @callback $cb ( Connection $conn, array $packet )
527
	 * @return void
528
	 */
529
	public function redirect(array $params, $cb) {
530
		$this->command("Action: Redirect\r\n" . $this->implodeParams($params), $cb);
531
	}
532
533
	/**
534
	 * Action: Originate
535
	 * Synopsis: Originate a call
536
	 * Privilege: call,all
537
	 * Description: first the Channel is rung. Then, when that answers, the Extension is dialled within the Context
538
	 *  to initiate the other end of the call.
539
	 * Variables: (Names marked with * are required)
540
	 * *Channel: Channel on which to originate the call (The same as you specify in the Dial application command)
541
	 * *Context: Context to use on connect (must use Exten & Priority with it)
542
	 * *Exten: Extension to use on connect (must use Context & Priority with it)
543
	 * *Priority: Priority to use on connect (must use Context & Exten with it)
544
	 * Timeout: Timeout (in milliseconds) for the originating connection to happen(defaults to 30000 milliseconds)
545
	 * *CallerID: CallerID to use for the call
546
	 * Variable: Channels variables to set (max 32). Variables will be set for both channels (local and connected).
547
	 * Account: Account code for the call
548
	 * Application: Application to use on connect (use Data for parameters)
549
	 * Data : Data if Application parameter is used
550
	 * ActionID: Optional Action id for message matching.
551
	 *
552
	 * @param array    $params
553
	 * @param callable $cb     Callback called when response received
554
	 * @callback $cb ( Connection $conn, array $packet )
555
	 * @return void
556
	 */
557
	public function originate(array $params, $cb) {
558
		$params['Async'] = 1;
559
560
		$this->command("Action: Originate\r\n" . $this->implodeParams($params), $cb);
561
	}
562
563
	/**
564
	 * Action: ExtensionState
565
	 * Synopsis: Get an extension's state.
566
	 * Description: function can be used to retrieve the state from any	hinted extension.
567
	 * Variables: (Names marked with * are required)
568
	 * *Exten: Extension to get state
569
	 * Context: Context for exten
570
	 * ActionID: Optional Action id for message matching.
571
	 *
572
	 * @param array    $params
573
	 * @param callable $cb     Callback called when response received
574
	 * @callback $cb ( Connection $conn, array $packet )
575
	 * @return void
576
	 */
577
	public function extensionState(array $params, $cb) {
578
		$this->command("Action: ExtensionState\r\n" . $this->implodeParams($params), $cb);
579
	}
580
581
	/**
582
	 * Action: Ping
583
	 * Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the
584
	 *   manager connection open.
585
	 * Variables: NONE
586
	 *
587
	 * @param callable $cb Callback called when response received
588
	 * @callback $cb ( Connection $conn, array $packet )
589
	 * @return void
590
	 */
591
	public function ping($cb) {
592
		$this->command("Action: Ping\r\n", $cb);
593
	}
594
595
	/**
596
	 * For almost any actions in Action: ListCommands
597
	 * Privilege: depends on $action
598
	 *
599
	 * @param string   $action    Action
600
	 * @param callable $cb        Callback called when response received
601
	 * @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...
602
	 * @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...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 160 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
603
	 * @callback $cb ( Connection $conn, array $packet )
604
	 * @return void
605
	 */
606
	public function action($action, $cb, array $params = null, array $assertion = null) {
607
		$action = trim($action);
608
609
		$this->command("Action: {$action}\r\n" . ($params ? $this->implodeParams($params) : ''), $cb, $assertion);
610
	}
611
612
	/**
613
	 * Action: Logoff
614
	 * Synopsis: Logoff Manager
615
	 * Privilege: <none>
616
	 * Description: Logoff this manager session
617
	 * Variables: NONE
618
	 *
619
	 * @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...
620
	 * @callback $cb ( Connection $conn, array $packet )
621
	 * @return void
622
	 */
623
	public function logoff($cb = null) {
624
		$this->command("Action: Logoff\r\n", $cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by parameter $cb on line 623 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...
625
	}
626
627
	/**
628
	 * Called when event occured
629
	 * @deprecated Replaced with ->bind('event', ...)
630
	 * @param callable $cb Callback
631
	 * @return void
632
	 */
633
	public function onEvent($cb) {
634
		$this->bind('event', $cb);
635
	}
636
637
	/**
638
	 * Sends arbitrary command
639
	 * @param string   $packet    A packet for sending by the connected client to Asterisk
640
	 * @param callable $cb        Callback called when response received
641
	 * @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...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 160 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
642
	 */
643
	protected function command($packet, $cb, $assertion = null) {
644
		if ($this->finished) {
645
			throw new ConnectionFinished;
646
		}
647
648
		if ($this->state !== self::CONN_STATE_HANDSHAKED_OK) {
649
			return;
650
		}
651
652
		$actionId = Daemon::uniqid();
653
654
		if (!is_callable($cb, true)) {
655
			$cb = false;
656
		}
657
658
		$this->callbacks[$actionId] = CallbackWrapper::wrap($cb);
0 ignored issues
show
Security Bug introduced by
It seems like $cb defined by false on line 655 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...
659
660
		if ($assertion !== null) {
661
			$this->assertions[$actionId] = $assertion;
662
		}
663
664
		$this->write($packet);
665
		$this->write('ActionID: ' . $actionId . "\r\n\r\n");
666
	}
667
668
	/**
669
	 * Generate AMI packet string from associative array provided
670
	 * @param  array  $params
671
	 * @return string
672
	 */
673
	protected function implodeParams(array $params) {
674
		$s = '';
675
676
		foreach ($params as $header => $value) {
677
			$s .= trim($header) . ": " . trim($value) . "\r\n";
678
		}
679
680
		return $s;
681
	}
682
}
683