Completed
Push — master ( fefc90...a4fa7b )
by Vasily
03:53
created

Connection::command()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 24
rs 8.5125
cc 5
eloc 13
nc 6
nop 3
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
		if ($this->state === self::CONN_STATE_HANDSHAKED_ERROR) {
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
								call_user_func($this->onChallenge, $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...
227
							}
228
						}
229 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...
230
							if ($packet['message'] === 'authentication accepted') {
231
								$this->state = self::CONN_STATE_HANDSHAKED_OK;
232
233
								Daemon::$process->log(__METHOD__ . ': Authentication ok. Connected to ' . parse_url($this->addr, PHP_URL_HOST));
234
235
								if ($this->onConnected) {
236
									$this->connected = true;
237
									$this->onConnected->executeAll($this);
238
									$this->onConnected = null;
239
								}
240
241
								$this->event('connected');
242
							}
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(__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...
249
250
						if ($this->onConnected) {
251
							$this->connected = false;
252
							$this->onConnected->executeAll($this);
253
							$this->onConnected = null;
254
						}
255
256
						$this->finish();
257
					}
258
259
					$this->packets = [];
260
				}
261
			}
262
			elseif ($this->state === self::CONN_STATE_HANDSHAKED_OK) {
263
				if ($this->instate === self::INPUT_STATE_END_OF_PACKET) {
264
					// Event
265
					if (isset($packet['event']) && !isset($packet['actionid'])) {
266
						$this->event('event_' . $packet['event'], $packet);
267
						$this->event('event', $packet);
268
					}
269
					// Response
270
					elseif (isset($packet['actionid'])) {
271
						$action_id =& $packet['actionid'];
272
273
						if (isset($this->callbacks[$action_id])) {
274
							if (isset($this->assertions[$action_id])) {
275
								$this->packets[$action_id][] = $packet;
276
								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...
277
									if (is_callable($this->callbacks[$action_id])) {
278
										$this->callbacks[$action_id]($this, $this->packets[$action_id]);
279
										unset($this->callbacks[$action_id]);
280
									}
281
282
									unset($this->assertions[$action_id]);
283
									unset($this->packets[$action_id]);
284
								}
285
							}
286
							else {
287
								if (is_callable($this->callbacks[$action_id])) {
288
									$this->callbacks[$action_id]($this, $packet);
289
									unset($this->callbacks[$action_id]);
290
								}
291
							}
292
						}
293
					}
294
295
					unset($packet);
296
					unset($this->packets[$this->cnt - 1]);
297
				}
298
			}
299
		}
300
	}
301
302
	/**
303
	 * Send authentication packet
304
	 * @return void
305
	 */
306
	protected function auth() {
307
		if ($this->state !== self::CONN_STATE_GOT_INITIAL_PACKET) {
308
			return;
309
		}
310
311
		if ($this->pool->config->authtype->value === 'md5') {
312
			$this->challenge(function ($conn, $challenge) {
313
				$packet      = "Action: Login\r\n"
314
						. "AuthType: MD5\r\n"
315
						. "Username: " . $this->username . "\r\n"
316
						. "Key: " . md5($challenge . $this->secret) . "\r\n"
317
						. "Events: on\r\n"
318
						. "\r\n";
319
				$this->state = self::CONN_STATE_LOGIN_PACKET_SENT_AFTER_CHALLENGE;
320
				$conn->write($packet);
321
			});
322
		}
323
		else {
324
			$this->login();
325
		}
326
	}
327
328
	/**
329
	 * Action: Login
330
	 * Synopsis: Login Manager
331
	 * Privilege: <none>
332
	 *
333
	 * @return void
334
	 */
335
	protected function login() {
336
		$this->state = self::CONN_STATE_LOGIN_PACKET_SENT;
337
		$this->write(
338
			"Action: login\r\n"
339
			. "Username: " . $this->username . "\r\n"
340
			. "Secret: " . $this->secret . "\r\n"
341
			. "Events: on\r\n"
342
			. "\r\n"
343
		);
344
	}
345
346
	/**
347
	 * Action: Challenge
348
	 * Synopsis: Generate Challenge for MD5 Auth
349
	 * Privilege: <none>
350
	 *
351
	 * @param  callable $cb
352
	 * @return void
353
	 */
354
	protected function challenge($cb) {
355
		$this->onChallenge = $cb;
356
		$this->state       = self::CONN_STATE_CHALLENGE_PACKET_SENT;
357
		$this->write(
358
			"Action: Challenge\r\n"
359
			. "AuthType: MD5\r\n"
360
			. "\r\n");
361
	}
362
363
	/**
364
	 * Action: SIPpeers
365
	 * Synopsis: List SIP peers (text format)
366
	 * Privilege: system,reporting,all
367
	 * Description: Lists SIP peers in text format with details on current status.
368
	 * Peerlist will follow as separate events, followed by a final event called
369
	 * PeerlistComplete.
370
	 * Variables:
371
	 * ActionID: <id>    Action ID for this transaction. Will be returned.
372
	 *
373
	 * @param callable $cb Callback called when response received
374
	 * @callback $cb ( Connection $conn, array $packet )
375
	 * @return void
376
	 */
377
	public function getSipPeers($cb) {
378
		$this->command("Action: SIPpeers\r\n", $cb, ['event' => 'peerlistcomplete']);
379
	}
380
381
	/**
382
	 * Action: IAXpeerlist
383
	 * Synopsis: List IAX Peers
384
	 * Privilege: system,reporting,all
385
	 *
386
	 * @param callable $cb Callback called when response received
387
	 * @callback $cb ( Connection $conn, array $packet )
388
	 * @return void
389
	 */
390
	public function getIaxPeers($cb) {
391
		$this->command("Action: IAXpeerlist\r\n", $cb, ['event' => 'peerlistcomplete']);
392
	}
393
394
	/**
395
	 * Action: GetConfig
396
	 * Synopsis: Retrieve configuration
397
	 * Privilege: system,config,all
398
	 * Description: A 'GetConfig' action will dump the contents of a configuration
399
	 * file by category and contents or optionally by specified category only.
400
	 * Variables: (Names marked with * are required)
401
	 *   *Filename: Configuration filename (e.g. foo.conf)
402
	 *   Category: Category in configuration file
403
	 *
404
	 * @param  string   $filename Filename
405
	 * @param  callable $cb       Callback called when response received
406
	 * @callback $cb ( Connection $conn, array $packet )
407
	 * @return void
408
	 */
409
	public function getConfig($filename, $cb) {
410
		$this->command("Action: GetConfig\r\nFilename: " . trim($filename) . "\r\n", $cb);
411
	}
412
413
	/**
414
	 * Action: GetConfigJSON
415
	 * Synopsis: Retrieve configuration
416
	 * Privilege: system,config,all
417
	 * Description: A 'GetConfigJSON' action will dump the contents of a configuration
418
	 * file by category and contents in JSON format.  This only makes sense to be used
419
	 * using rawman over the HTTP interface.
420
	 * Variables:
421
	 *    Filename: Configuration filename (e.g. foo.conf)
422
	 *
423
	 * @param  string   $filename Filename
424
	 * @param callable $cb Callback called when response received
425
	 * @callback $cb ( Connection $conn, array $packet )
426
	 * @return void
427
	 */
428
	public function getConfigJSON($filename, $cb) {
429
		$this->command("Action: GetConfigJSON\r\nFilename: " . trim($filename) . "\r\n", $cb);
430
	}
431
432
	/**
433
	 * Action: Setvar
434
	 * Synopsis: Set Channel Variable
435
	 * Privilege: call,all
436
	 * Description: Set a global or local channel variable.
437
	 * Variables: (Names marked with * are required)
438
	 * Channel: Channel to set variable for
439
	 *  *Variable: Variable name
440
	 *  *Value: Value
441
	 * 
442
	 * @param string   $channel
443
	 * @param string   $variable
444
	 * @param string   $value
445
	 * @param callable $cb
446
	 * @callback $cb ( Connection $conn, array $packet )
447
	 * @return void
448
	 */
449
	public function setVar($channel, $variable, $value, $cb) {
450
		$cmd = "Action: SetVar\r\n";
451
452
		if ($channel) {
453
			$cmd .= "Channel: " . trim($channel) . "\r\n";
454
		}
455
456
		if (isset($variable, $value)) {
457
			$cmd .= "Variable: " . trim($variable) . "\r\n"
458
					. "Value: " . trim($value) . "\r\n";
459
460
			$this->command($cmd, $cb);
461
		}
462
	}
463
464
	/**
465
	 * Action: CoreShowChannels
466
	 * Synopsis: List currently active channels
467
	 * Privilege: system,reporting,all
468
	 * Description: List currently defined channels and some information
469
	 *        about them.
470
	 * Variables:
471
	 *        ActionID: Optional Action id for message matching.
472
	 *
473
	 * @param callable $cb
474
	 * @callback $cb ( Connection $conn, array $packet )
475
	 * @return void
476
	 */
477
	public function coreShowChannels($cb) {
478
		$this->command("Action: CoreShowChannels\r\n", $cb,
479
					   ['event' => 'coreshowchannelscomplete', 'eventlist' => 'complete']
480
		);
481
	}
482
483
	/**
484
	 * Action: Status
485
	 * Synopsis: Lists channel status
486
	 * Privilege: system,call,reporting,all
487
	 * Description: Lists channel status along with requested channel vars.
488
	 * Variables: (Names marked with * are required)
489
	 *Channel: Name of the channel to query for status
490
	 *    Variables: Comma ',' separated list of variables to include
491
	 * ActionID: Optional ID for this transaction
492
	 * Will return the status information of each channel along with the
493
	 * value for the specified channel variables.
494
	 *
495
	 * @param  callable $cb
496
	 * @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...
497
	 * @callback $cb ( Connection $conn, array $packet )
498
	 * @return void
499
	 */
500
	public function status($cb, $channel = null) {
501
		$cmd = "Action: Status\r\n";
502
503
		if ($channel !== null) {
504
			$cmd .= 'Channel: ' . trim($channel) . "\r\n";
505
		}
506
507
		$this->command($cmd, $cb, ['event' => 'statuscomplete']);
508
	}
509
510
	/**
511
	 * Action: Redirect
512
	 * Synopsis: Redirect (transfer) a call
513
	 * Privilege: call,all
514
	 * Description: Redirect (transfer) a call.
515
	 * Variables: (Names marked with * are required)
516
	 * *Channel: Channel to redirect
517
	 *  ExtraChannel: Second call leg to transfer (optional)
518
	 * *Exten: Extension to transfer to
519
	 * *Context: Context to transfer to
520
	 * *Priority: Priority to transfer to
521
	 * ActionID: Optional Action id for message matching.
522
	 *
523
	 * @param array    $params
524
	 * @param callable $cb     Callback called when response received
525
	 * @callback $cb ( Connection $conn, array $packet )
526
	 * @return void
527
	 */
528
	public function redirect(array $params, $cb) {
529
		$this->command("Action: Redirect\r\n" . $this->implodeParams($params), $cb);
530
	}
531
532
	/**
533
	 * Action: Originate
534
	 * Synopsis: Originate a call
535
	 * Privilege: call,all
536
	 * Description: first the Channel is rung. Then, when that answers, the Extension is dialled within the Context
537
	 *  to initiate the other end of the call.
538
	 * Variables: (Names marked with * are required)
539
	 * *Channel: Channel on which to originate the call (The same as you specify in the Dial application command)
540
	 * *Context: Context to use on connect (must use Exten & Priority with it)
541
	 * *Exten: Extension to use on connect (must use Context & Priority with it)
542
	 * *Priority: Priority to use on connect (must use Context & Exten with it)
543
	 * Timeout: Timeout (in milliseconds) for the originating connection to happen(defaults to 30000 milliseconds)
544
	 * *CallerID: CallerID to use for the call
545
	 * Variable: Channels variables to set (max 32). Variables will be set for both channels (local and connected).
546
	 * Account: Account code for the call
547
	 * Application: Application to use on connect (use Data for parameters)
548
	 * Data : Data if Application parameter is used
549
	 * ActionID: Optional Action id for message matching.
550
	 *
551
	 * @param array    $params
552
	 * @param callable $cb     Callback called when response received
553
	 * @callback $cb ( Connection $conn, array $packet )
554
	 * @return void
555
	 */
556
	public function originate(array $params, $cb) {
557
		$params['Async'] = 1;
558
559
		$this->command("Action: Originate\r\n" . $this->implodeParams($params), $cb);
560
	}
561
562
	/**
563
	 * Action: ExtensionState
564
	 * Synopsis: Get an extension's state.
565
	 * Description: function can be used to retrieve the state from any	hinted extension.
566
	 * Variables: (Names marked with * are required)
567
	 * *Exten: Extension to get state
568
	 * Context: Context for exten
569
	 * ActionID: Optional Action id for message matching.
570
	 *
571
	 * @param array    $params
572
	 * @param callable $cb     Callback called when response received
573
	 * @callback $cb ( Connection $conn, array $packet )
574
	 * @return void
575
	 */
576
	public function extensionState(array $params, $cb) {
577
		$this->command("Action: ExtensionState\r\n" . $this->implodeParams($params), $cb);
578
	}
579
580
	/**
581
	 * Action: Ping
582
	 * Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the
583
	 *   manager connection open.
584
	 * Variables: NONE
585
	 *
586
	 * @param callable $cb Callback called when response received
587
	 * @callback $cb ( Connection $conn, array $packet )
588
	 * @return void
589
	 */
590
	public function ping($cb) {
591
		$this->command("Action: Ping\r\n", $cb);
592
	}
593
594
	/**
595
	 * For almost any actions in Action: ListCommands
596
	 * Privilege: depends on $action
597
	 *
598
	 * @param string   $action    Action
599
	 * @param callable $cb        Callback called when response received
600
	 * @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...
601
	 * @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...
602
	 * @callback $cb ( Connection $conn, array $packet )
603
	 * @return void
604
	 */
605
	public function action($action, $cb, array $params = null, array $assertion = null) {
606
		$action = trim($action);
607
608
		$this->command("Action: {$action}\r\n" . ($params ? $this->implodeParams($params) : ''), $cb, $assertion);
609
	}
610
611
	/**
612
	 * Action: Logoff
613
	 * Synopsis: Logoff Manager
614
	 * Privilege: <none>
615
	 * Description: Logoff this manager session
616
	 * Variables: NONE
617
	 *
618
	 * @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...
619
	 * @callback $cb ( Connection $conn, array $packet )
620
	 * @return void
621
	 */
622
	public function logoff($cb = null) {
623
		$this->command("Action: Logoff\r\n", $cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by parameter $cb on line 622 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...
624
	}
625
626
	/**
627
	 * Called when event occured
628
	 * @deprecated Replaced with ->bind('event', ...)
629
	 * @param callable $cb Callback
630
	 * @return void
631
	 */
632
	public function onEvent($cb) {
633
		$this->bind('event', $cb);
634
	}
635
636
	/**
637
	 * Sends arbitrary command
638
	 * @param string   $packet    A packet for sending by the connected client to Asterisk
639
	 * @param callable $cb        Callback called when response received
640
	 * @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...
641
	 */
642
	protected function command($packet, $cb, $assertion = null) {
643
		if ($this->finished) {
644
			throw new ConnectionFinished;
645
		}
646
647
		if ($this->state !== self::CONN_STATE_HANDSHAKED_OK) {
648
			return;
649
		}
650
651
		$actionId = Daemon::uniqid();
652
653
		if (!is_callable($cb, true)) {
654
			$cb = false;
655
		}
656
657
		$this->callbacks[$actionId] = CallbackWrapper::wrap($cb);
0 ignored issues
show
Security Bug introduced by
It seems like $cb defined by false on line 654 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...
658
659
		if ($assertion !== null) {
660
			$this->assertions[$actionId] = $assertion;
661
		}
662
663
		$this->write($packet);
664
		$this->write('ActionID: ' . $actionId . "\r\n\r\n");
665
	}
666
667
	/**
668
	 * Generate AMI packet string from associative array provided
669
	 * @param  array  $params
670
	 * @return string
671
	 */
672
	protected function implodeParams(array $params) {
673
		$s = '';
674
675
		foreach ($params as $header => $value) {
676
			$s .= trim($header) . ": " . trim($value) . "\r\n";
677
		}
678
679
		return $s;
680
	}
681
}
682