AGI::text_input()   F
last analyzed

Complexity

Conditions 18
Paths 49

Size

Total Lines 125
Code Lines 120

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 18
eloc 120
nc 49
nop 1
dl 0
loc 125
rs 3.8933
c 0
b 0
f 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
3
namespace Inok\phpagi;
4
5
/**
6
 * phpagi.php : PHP AGI Functions for Asterisk
7
 * @see https://github.com/welltime/phpagi
8
 * @filesource http://phpagi.sourceforge.net/
9
 *
10
 * $Id: phpagi.php,v 2.20 2010/09/30 02:21:00 masham Exp $
11
 *
12
 * Copyright (c) 2003 - 2010 Matthew Asham <[email protected]>, David Eder <[email protected]> and others
13
 * All Rights Reserved.
14
 *
15
 * This software is released under the terms of the GNU Lesser General Public License v2.1
16
 * A copy of which is available from http://www.gnu.org/copyleft/lesser.html
17
 *
18
 * We would be happy to list your phpagi based application on the phpagi
19
 * website.  Drop me an Email if you'd like us to list your program.
20
 *
21
 *
22
 * Written for PHP 4.3.4, should work with older PHP 4.x versions.
23
 *
24
 * Please submit bug reports, patches, etc to https://github.com/welltime/phpagi
25
 *
26
 *
27
 * @package phpAGI
28
 * @version 2.20
29
 */
30
31
/**
32
 * AGI class
33
 *
34
 * @package phpAGI
35
 * @link http://www.voip-info.org/wiki-Asterisk+agi
36
 * @example examples/dtmf.php Get DTMF tones from the user and say the digits
37
 * @example examples/input.php Get text input from the user and say it back
38
 * @example examples/ping.php Ping an IP address
39
 */
40
class AGI
41
{
42
  /**
43
   * Request variables read in on initialization.
44
   *
45
   * Often contains any/all of the following:
46
   *   agi_request - name of agi script
47
   *   agi_channel - current channel
48
   *   agi_language - current language
49
   *   agi_type - channel type (SIP, ZAP, IAX, ...)
50
   *   agi_uniqueid - unique id based on unix time
51
   *   agi_callerid - callerID string
52
   *   agi_dnid - dialed number id
53
   *   agi_rdnis - referring DNIS number
54
   *   agi_context - current context
55
   *   agi_extension - extension dialed
56
   *   agi_priority - current priority
57
   *   agi_enhanced - value is 1.0 if started as an EAGI script
58
   *   agi_accountcode - set by SetAccount in the dialplan
59
   *   agi_network - value is yes if this is a fastagi
60
   *   agi_network_script - name of the script to execute
61
   *
62
   * NOTE: program arguments are still in $_SERVER['argv'].
63
   *
64
   * @var array
65
   * @access public
66
   */
67
  public $request;
68
69
  /**
70
   * Config variables
71
   *
72
   * @var array
73
   * @access public
74
   */
75
  public $config;
76
77
  /**
78
   * Asterisk Manager
79
   *
80
   * @var AGI_AsteriskManager
81
   * @access public
82
   */
83
  public $asm;
84
85
  /**
86
   * Input Stream
87
   *
88
   * @access private
89
   */
90
  private $in;
91
92
  /**
93
   * Output Stream
94
   *
95
   * @access private
96
   */
97
  private $out;
98
99
  /**
100
   * Audio Stream
101
   *
102
   * @access public
103
   */
104
  public $audio = null;
105
106
107
  /**
108
   * Application option delimiter
109
   *
110
   * @access public
111
   */
112
  public $option_delim = ",";
113
114
  private $defaultConfig = ["error_handler" => true,
115
                            "debug" => false,
116
                            "admin" => null,
117
                            "tempdir" => AGI_Others::AST_TMP_DIR];
118
119
  /**
120
   * Constructor
121
   *
122
   * @param string $config is the name of the config file to parse
123
   * @param array $optconfig is an array of configuration vars and values, stuffed into $this->config['phpagi']
124
   */
125
  public function __construct($config = null, array $optconfig = []) {
126
    // load config
127
    if (!is_null($config) && file_exists($config)) {
128
      $this->config = parse_ini_file($config, true);
129
    } elseif (file_exists(AGI_Others::DEFAULT_PHPAGI_CONFIG)) {
130
      $this->config = parse_ini_file(AGI_Others::DEFAULT_PHPAGI_CONFIG, true);
131
    }
132
133
    // If optconfig is specified, stuff values and vars into 'phpagi' config array.
134
    foreach ($optconfig as $var => $val) {
135
      $this->config['phpagi'][$var] = $val;
136
    }
137
138
    // add default values to config for uninitialized values
139
    foreach ($this->defaultConfig as $name => $value) {
140
      $this->config["phpagi"][$name] = $this->config["phpagi"][$name] ?? $value;
141
    }
142
143
    // festival TTS config
144
    $this->config['festival']['text2wave'] = $this->config['festival']['text2wave'] ?? $this->which('text2wave');
145
146
    // swift TTS config
147
    $this->config['cepstral']['swift'] = $this->config['cepstral']['swift'] ?? $this->which('swift');
148
149
    ob_implicit_flush();
150
151
    // open stdin & stdout
152
    $this->in = defined('STDIN') ? STDIN : fopen('php://stdin', 'r');
153
    $this->out = defined('STDOUT') ? STDOUT : fopen('php://stdout', 'w');
154
155
    // initialize error handler
156
    if ($this->config['phpagi']['error_handler']) {
157
      set_error_handler('\\Inok\\phpagi\\AGI_Others::phpagi_error_handler');
158
      AGI_Others::$phpagi_error_handler_email = $this->config['phpagi']['admin'];
159
      error_reporting(E_ALL);
160
    }
161
162
    // make sure temp folder exists
163
    $this->make_folder($this->config['phpagi']['tempdir']);
164
165
    // read the request
166
    $str = fgets($this->in);
167
    while ($str != "\n") {
168
      $this->request[substr($str, 0, strpos($str, ':'))] = trim(substr($str, strpos($str, ':') + 1));
169
      $str = fgets($this->in);
170
    }
171
172
    // open audio if agi detected
173
    if ($this->request['agi_enhanced'] == '1.0') {
174
      if (file_exists('/proc/' . getmypid() . '/fd/3')) {
175
        $this->audio = fopen('/proc/' . getmypid() . '/fd/3', 'r');
176
      } elseif (file_exists('/dev/fd/3')) {
177
        // may need to mount fdescfs
178
        $this->audio = fopen('/dev/fd/3', 'r');
179
      } else {
180
        $this->conlog('Unable to open audio stream');
181
      }
182
183
      if ($this->audio) {
184
        stream_set_blocking($this->audio, 0);
185
      }
186
    }
187
188
    $this->conlog('AGI Request:');
189
    $this->conlog(/** @scrutinizer ignore-type */ print_r($this->request, true));
190
    $this->conlog('PHPAGI internal configuration:');
191
    $this->conlog(/** @scrutinizer ignore-type */ print_r($this->config, true));
192
  }
193
194
  // *********************************************************************************************************
195
  // **                             COMMANDS                                                                                            **
196
  // *********************************************************************************************************
197
198
  /**
199
   * Answer channel if not already in answer state.
200
   *
201
   * @link http://www.voip-info.org/wiki-answer
202
   * @example examples/dtmf.php Get DTMF tones from the user and say the digits
203
   * @example examples/input.php Get text input from the user and say it back
204
   * @example examples/ping.php Ping an IP address
205
   *
206
   * @return array, see evaluate for return information.  ['result'] is 0 on success, -1 on failure.
207
   */
208
  function answer(): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
209
    return $this->evaluate('ANSWER');
210
  }
211
212
  /**
213
   * Get the status of the specified channel. If no channel name is specified, return the status of the current channel.
214
   *
215
   * @link http://www.voip-info.org/wiki-channel+status
216
   * @param string $channel
217
   * @return array, see evaluate for return information. ['data'] contains description.
218
   */
219
  function channel_status(string $channel = ''): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
220
    $ret = $this->evaluate("CHANNEL STATUS $channel");
221
    switch ($ret['result']) {
222
      case -1:
223
        $ret['data'] = trim("There is no channel that matches $channel");
224
        break;
225
      case AGI_Others::AST_STATE_DOWN:
226
        $ret['data'] = 'Channel is down and available';
227
        break;
228
      case AGI_Others::AST_STATE_RESERVED:
229
        $ret['data'] = 'Channel is down, but reserved';
230
        break;
231
      case AGI_Others::AST_STATE_OFFHOOK:
232
        $ret['data'] = 'Channel is off hook';
233
        break;
234
      case AGI_Others::AST_STATE_DIALING:
235
        $ret['data'] = 'Digits (or equivalent) have been dialed';
236
        break;
237
      case AGI_Others::AST_STATE_RING:
238
        $ret['data'] = 'Line is ringing';
239
        break;
240
      case AGI_Others::AST_STATE_RINGING:
241
        $ret['data'] = 'Remote end is ringing';
242
        break;
243
      case AGI_Others::AST_STATE_UP:
244
        $ret['data'] = 'Line is up';
245
        break;
246
      case AGI_Others::AST_STATE_BUSY:
247
        $ret['data'] = 'Line is busy';
248
        break;
249
      case AGI_Others::AST_STATE_DIALING_OFFHOOK:
250
        $ret['data'] = 'Digits (or equivalent) have been dialed while offhook';
251
        break;
252
      case AGI_Others::AST_STATE_PRERING:
253
        $ret['data'] = 'Channel has detected an incoming call and is waiting for ring';
254
        break;
255
      default:
256
        $ret['data'] = "Unknown ({$ret['result']})";
257
        break;
258
    }
259
    return $ret;
260
  }
261
262
  /**
263
   * Deletes an entry in the Asterisk database for a given family and key.
264
   *
265
   * @link http://www.voip-info.org/wiki-database+del
266
   * @param string $family
267
   * @param string $key
268
   * @return array, see evaluate for return information. ['result'] is 1 on success, 0 otherwise.
269
   */
270
  function database_del(string $family, string $key): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
271
    return $this->evaluate("DATABASE DEL \"$family\" \"$key\"");
272
  }
273
274
  /**
275
   * Deletes a family or specific keytree within a family in the Asterisk database.
276
   *
277
   * @link http://www.voip-info.org/wiki-database+deltree
278
   * @param string $family
279
   * @param string $keytree
280
   * @return array, see evaluate for return information. ['result'] is 1 on success, 0 otherwise.
281
   */
282
  function database_deltree(string $family, string $keytree = ''): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
283
    $cmd = "DATABASE DELTREE \"$family\"";
284
    if ($keytree != '') {
285
      $cmd .= " \"$keytree\"";
286
    }
287
    return $this->evaluate($cmd);
288
  }
289
290
  /**
291
   * Retrieves an entry in the Asterisk database for a given family and key.
292
   *
293
   * @link http://www.voip-info.org/wiki-database+get
294
   * @param string $family
295
   * @param string $key
296
   * @return array, see evaluate for return information. ['result'] is 1 on success, 0 failure. ['data'] holds the value
297
   */
298
  function database_get(string $family, string $key): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
299
    return $this->evaluate("DATABASE GET \"$family\" \"$key\"");
300
  }
301
302
  /**
303
   * Adds or updates an entry in the Asterisk database for a given family, key, and value.
304
   *
305
   * @param string $family
306
   * @param string $key
307
   * @param string $value
308
   * @return array, see evaluate for return information. ['result'] is 1 on success, 0 otherwise
309
   */
310
  function database_put(string $family, string $key, string $value): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
311
    $value = str_replace("\n", '\n', addslashes($value));
312
    return $this->evaluate("DATABASE PUT \"$family\" \"$key\" \"$value\"");
313
  }
314
315
316
  /**
317
   * Sets a global variable, using Asterisk 1.6 syntax.
318
   *
319
   * @link http://www.voip-info.org/wiki/view/Asterisk+cmd+Set
320
   *
321
   * @param string $pVariable
322
   * @param string|int|float $pValue
323
   * @return array, see evaluate for return information. ['result'] is 1 on success, 0 otherwise
324
   */
325
  function set_global_var(string $pVariable, $pValue): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
326
    if (is_numeric($pValue)) {
327
      return $this->evaluate("Set({$pVariable}={$pValue},g);");
328
    }
329
    return $this->evaluate("Set({$pVariable}=\"{$pValue}\",g);");
330
  }
331
332
333
  /**
334
   * Sets a variable, using Asterisk 1.6 syntax.
335
   *
336
   * @link http://www.voip-info.org/wiki/view/Asterisk+cmd+Set
337
   *
338
   * @param string $pVariable
339
   * @param string|int|float $pValue
340
   * @return array, see evaluate for return information. ['result'] is 1 on success, 0 otherwise
341
   */
342
  function set_var(string $pVariable, $pValue): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
343
    if (is_numeric($pValue)) {
344
      return $this->evaluate("Set({$pVariable}={$pValue});");
345
    }
346
    return $this->evaluate("Set({$pVariable}=\"{$pValue}\");");
347
  }
348
349
350
  /**
351
   * Executes the specified Asterisk application with given options.
352
   *
353
   * @link http://www.voip-info.org/wiki-exec
354
   * @link http://www.voip-info.org/wiki-Asterisk+-+documentation+of+application+commands
355
   * @param string $application
356
   * @param mixed $options
357
   * @return array, see evaluate for return information. ['result'] is whatever the application returns, or -2 on failure to find application
358
   */
359
  function exec(string $application, $options): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
360
    if (is_array($options)) {
361
      $options = join('|', $options);
362
    }
363
    return $this->evaluate("EXEC $application $options");
364
  }
365
366
  /**
367
   * Plays the given file and receives DTMF data.
368
   *
369
   * This is similar to STREAM FILE, but this command can accept and return many DTMF digits,
370
   * while STREAM FILE returns immediately after the first DTMF digit is detected.
371
   *
372
   * Asterisk looks for the file to play in /var/lib/asterisk/sounds by default.
373
   *
374
   * If the user doesn't press any keys when the message plays, there is $timeout milliseconds
375
   * of silence then the command ends.
376
   *
377
   * The user has the opportunity to press a key at any time during the message or the
378
   * post-message silence. If the user presses a key while the message is playing, the
379
   * message stops playing. When the first key is pressed a timer starts counting for
380
   * $timeout milliseconds. Every time the user presses another key the timer is restarted.
381
   * The command ends when the counter goes to zero or the maximum number of digits is entered,
382
   * whichever happens first.
383
   *
384
   * If you don't specify a time out then a default timeout of 2000 is used following a pressed
385
   * digit. If no digits are pressed then 6 seconds of silence follow the message.
386
   *
387
   * If you don't specify $max_digits then the user can enter as many digits as they want.
388
   *
389
   * Pressing the # key has the same effect as the timer running out: the command ends and
390
   * any previously keyed digits are returned. A side effect of this is that there is no
391
   * way to read a # key using this command.
392
   *
393
   * @param string $filename file to play. Do not include file extension.
394
   * @param integer $timeout milliseconds
395
   * @param integer $max_digits
396
   * @return array, see evaluate for return information. ['result'] holds the digits and ['data'] holds the timeout if present.
397
   *
398
   * This differs from other commands with return DTMF as numbers representing ASCII characters.
399
   * @example examples/ping.php Ping an IP address
400
   *
401
   * @link http://www.voip-info.org/wiki-get+data
402
   */
403
  function get_data(string $filename, $timeout = null, $max_digits = null): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
404
    return $this->evaluate(rtrim("GET DATA $filename $timeout $max_digits"));
405
  }
406
407
  /**
408
   * Fetch the value of a variable.
409
   *
410
   * Does not work with global variables. Does not work with some variables that are generated by modules.
411
   *
412
   * @link http://www.voip-info.org/wiki-get+variable
413
   * @link http://www.voip-info.org/wiki-Asterisk+variables
414
   * @param string $variable name
415
   * @param boolean $getvalue return the value only
416
   * @return mixed, see evaluate for return information. ['result'] is 0 if variable hasn't been set, 1 if it has. ['data'] holds the value. returns value if $getvalue is TRUE
417
   */
418
  function get_variable(string $variable, bool $getvalue = false) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
419
    $res = $this->evaluate("GET VARIABLE $variable");
420
    return $getvalue ? $res['data'] : $res;
421
  }
422
423
424
  /**
425
   * Fetch the value of a full variable.
426
   *
427
   *
428
   * @link http://www.voip-info.org/wiki/view/get+full+variable
429
   * @link http://www.voip-info.org/wiki-Asterisk+variables
430
   * @param string $variable name
431
   * @param string $channel channel
432
   * @param boolean $getvalue return the value only
433
   * @return mixed, see evaluate for return information. ['result'] is 0 if variable hasn't been set, 1 if it has. ['data'] holds the value.  returns value if $getvalue is TRUE
434
   */
435
  function get_fullvariable(string $variable, $channel = false, bool $getvalue = false) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
436
    $req = ($channel == false) ? $variable : $variable . ' ' . $channel;
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $channel of type false|string against false; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
437
    $res = $this->evaluate('GET FULL VARIABLE ' . $req);
438
    return $getvalue ? $res['data'] : $res;
439
440
  }
441
442
  /**
443
   * Hangup the specified channel. If no channel name is given, hang up the current channel.
444
   *
445
   * With power comes responsibility. Hanging up channels other than your own isn't something
446
   * that is done routinely. If you are not sure why you are doing so, then don't.
447
   *
448
   * @link http://www.voip-info.org/wiki-hangup
449
   * @example examples/dtmf.php Get DTMF tones from the user and say the digits
450
   * @example examples/input.php Get text input from the user and say it back
451
   * @example examples/ping.php Ping an IP address
452
   *
453
   * @param string $channel
454
   * @return array, see evaluate for return information. ['result'] is 1 on success, -1 on failure.
455
   */
456
  function hangup(string $channel = ''): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
457
    return $this->evaluate("HANGUP $channel");
458
  }
459
460
  /**
461
   * Does nothing.
462
   *
463
   * @link http://www.voip-info.org/wiki-noop
464
   * @param string $string
465
   * @return array, see evaluate for return information.
466
   */
467
  function noop(string $string = ""): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
468
    return $this->evaluate("NOOP \"$string\"");
469
  }
470
471
  /**
472
   * Receive a character of text from a connected channel. Waits up to $timeout milliseconds for
473
   * a character to arrive, or infinitely if $timeout is zero.
474
   *
475
   * @link http://www.voip-info.org/wiki-receive+char
476
   * @param integer $timeout milliseconds
477
   * @return array, see evaluate for return information. ['result'] is 0 on timeout or not supported, -1 on failure. Otherwise
478
   * it is the decimal value of the DTMF tone. Use chr() to convert to ASCII.
479
   */
480
  function receive_char(int $timeout = -1): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
481
    return $this->evaluate("RECEIVE CHAR $timeout");
482
  }
483
484
  /**
485
   * Record sound to a file until an acceptable DTMF digit is received or a specified amount of
486
   * time has passed. Optionally the file BEEP is played before recording begins.
487
   *
488
   * @link http://www.voip-info.org/wiki-record+file
489
   * @param string $file to record, without extension, often created in /var/lib/asterisk/sounds
490
   * @param string $format of the file. GSM and WAV are commonly used formats. MP3 is read-only and thus cannot be used.
491
   * @param string $escape_digits
492
   * @param integer $timeout is the maximum record time in milliseconds, or -1 for no timeout.
493
   * @param integer $offset to seek to without exceeding the end of the file.
494
   * @param boolean $beep
495
   * @param integer $silence number of seconds of silence allowed before the function returns despite the
496
   * lack of dtmf digits or reaching timeout.
497
   * @return array, see evaluate for return information. ['result'] is -1 on error, 0 on hangup, otherwise a decimal value of the
498
   * DTMF tone. Use chr() to convert to ASCII.
499
   */
500
  function record_file(string $file, string $format, string $escape_digits = '', int $timeout = -1,
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
501
                       $offset = null, bool $beep = false, $silence = null): array {
502
    $cmd = trim("RECORD FILE $file $format \"$escape_digits\" $timeout $offset");
503
    if ($beep) {
504
      $cmd .= ' BEEP';
505
    }
506
    if (!is_null($silence)) {
507
      $cmd .= " s=$silence";
508
    }
509
    return $this->evaluate($cmd);
510
  }
511
512
  /**
513
   * Say a given character string, returning early if any of the given DTMF digits are received on the channel.
514
   *
515
   * @link https://www.voip-info.org/say-alpha
516
   * @param string $text
517
   * @param string $escape_digits
518
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
519
   * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
520
   */
521
  function say_alpha(string $text, string $escape_digits = ''): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
522
    return $this->evaluate("SAY ALPHA $text \"$escape_digits\"");
523
  }
524
525
  /**
526
   * Say the given digit string, returning early if any of the given DTMF escape digits are received on the channel.
527
   *
528
   * @link http://www.voip-info.org/wiki-say+digits
529
   * @param integer $digits
530
   * @param string $escape_digits
531
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
532
   * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
533
   */
534
  function say_digits(int $digits, string $escape_digits = ''): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
535
    return $this->evaluate("SAY DIGITS $digits \"$escape_digits\"");
536
  }
537
538
  /**
539
   * Say the given number, returning early if any of the given DTMF escape digits are received on the channel.
540
   *
541
   * @link http://www.voip-info.org/wiki-say+number
542
   * @param integer $number
543
   * @param string $escape_digits
544
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
545
   * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
546
   */
547
  function say_number(int $number, string $escape_digits = ''): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
548
    return $this->evaluate("SAY NUMBER $number \"$escape_digits\"");
549
  }
550
551
  /**
552
   * Say the given character string, returning early if any of the given DTMF escape digits are received on the channel.
553
   *
554
   * @link http://www.voip-info.org/wiki-say+phonetic
555
   * @param string $text
556
   * @param string $escape_digits
557
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
558
   * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
559
   */
560
  function say_phonetic(string $text, string $escape_digits = ''): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
561
    return $this->evaluate("SAY PHONETIC $text \"$escape_digits\"");
562
  }
563
564
  /**
565
   * Say a given time, returning early if any of the given DTMF escape digits are received on the channel.
566
   *
567
   * @link http://www.voip-info.org/wiki-say+time
568
   * @param integer $time number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC).
569
   * @param string $escape_digits
570
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
571
   * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
572
   */
573
  function say_time($time = null, string $escape_digits = ''): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
574
    if (is_null($time)) {
575
      $time = time();
576
    }
577
    return $this->evaluate("SAY TIME $time \"$escape_digits\"");
578
  }
579
580
  /**
581
   * Send the specified image on a channel.
582
   *
583
   * Most channels do not support the transmission of images.
584
   *
585
   * @link http://www.voip-info.org/wiki-send+image
586
   * @param string $image without extension, often in /var/lib/asterisk/images
587
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if the image is sent or
588
   * channel does not support image transmission.
589
   */
590
  function send_image(string $image): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
591
    return $this->evaluate("SEND IMAGE $image");
592
  }
593
594
  /**
595
   * Send the given text to the connected channel.
596
   *
597
   * Most channels do not support transmission of text.
598
   *
599
   * @link http://www.voip-info.org/wiki-send+text
600
   * @param $text
601
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if the text is sent or
602
   * channel does not support text transmission.
603
   */
604
  function send_text($text): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
605
    return $this->evaluate("SEND TEXT \"$text\"");
606
  }
607
608
  /**
609
   * Cause the channel to automatically hangup at $time seconds in the future.
610
   * If $time is 0 then the auto hangup feature is disabled on this channel.
611
   *
612
   * If the channel is hangup prior to $time seconds, this setting has no effect.
613
   *
614
   * @link http://www.voip-info.org/wiki-set+autohangup
615
   * @param integer $time until automatic hangup
616
   * @return array, see evaluate for return information.
617
   */
618
  function set_autohangup(int $time = 0): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
619
    return $this->evaluate("SET AUTOHANGUP $time");
620
  }
621
622
  /**
623
   * Changes the caller ID of the current channel.
624
   *
625
   * @link http://www.voip-info.org/wiki-set+callerid
626
   * @param string $cid example: "John Smith"<1234567>
627
   * This command will let you take liberties with the <caller ID specification> but the format shown in the example above works
628
   * well: the name enclosed in double quotes followed immediately by the number inside angle brackets. If there is no name then
629
   * you can omit it. If the name contains no spaces you can omit the double quotes around it. The number must follow the name
630
   * immediately; don't put a space between them. The angle brackets around the number are necessary; if you omit them the
631
   * number will be considered to be part of the name.
632
   * @return array, see evaluate for return information.
633
   */
634
  function set_callerid(string $cid): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
635
    return $this->evaluate("SET CALLERID $cid");
636
  }
637
638
  /**
639
   * Sets the context for continuation upon exiting the application.
640
   *
641
   * Setting the context does NOT automatically reset the extension and the priority; if you want to start at the top of the new
642
   * context you should set extension and priority yourself.
643
   *
644
   * If you specify a non-existent context you receive no error indication (['result'] is still 0) but you do get a
645
   * warning message on the Asterisk console.
646
   *
647
   * @link http://www.voip-info.org/wiki-set+context
648
   * @param string $context
649
   * @return array, see evaluate for return information.
650
   */
651
  function set_context(string $context): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
652
    return $this->evaluate("SET CONTEXT $context");
653
  }
654
655
  /**
656
   * Set the extension to be used for continuation upon exiting the application.
657
   *
658
   * Setting the extension does NOT automatically reset the priority. If you want to start with the first priority of the
659
   * extension you should set the priority yourself.
660
   *
661
   * If you specify a non-existent extension you receive no error indication (['result'] is still 0) but you do
662
   * get a warning message on the Asterisk console.
663
   *
664
   * @link http://www.voip-info.org/wiki-set+extension
665
   * @param string $extension
666
   * @return array, see evaluate for return information.
667
   */
668
  function set_extension(string $extension): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
669
    return $this->evaluate("SET EXTENSION $extension");
670
  }
671
672
  /**
673
   * Enable/Disable Music on hold generator.
674
   *
675
   * @link http://www.voip-info.org/wiki-set+music
676
   * @param boolean $enabled
677
   * @param string $class
678
   * @return array, see evaluate for return information.
679
   */
680
  function set_music(bool $enabled = true, string $class = ''): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
681
    $enabled = ($enabled) ? 'ON' : 'OFF';
682
    return $this->evaluate("SET MUSIC $enabled $class");
683
  }
684
685
  /**
686
   * Set the priority to be used for continuation upon exiting the application.
687
   *
688
   * If you specify a non-existent priority you receive no error indication (['result'] is still 0)
689
   * and no warning is issued on the Asterisk console.
690
   *
691
   * @link http://www.voip-info.org/wiki-set+priority
692
   * @param integer $priority
693
   * @return array, see evaluate for return information.
694
   */
695
  function set_priority(int $priority): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
696
    return $this->evaluate("SET PRIORITY $priority");
697
  }
698
699
  /**
700
   * Sets a variable to the specified value. The variables so created can later be used by later using ${<variablename>}
701
   * in the dialplan.
702
   *
703
   * These variables live in the channel Asterisk creates when you pickup a phone and as such they are both local and temporary.
704
   * Variables created in one channel can not be accessed by another channel. When you hang up the phone, the channel is deleted
705
   * and any variables in that channel are deleted as well.
706
   *
707
   * @link http://www.voip-info.org/wiki-set+variable
708
   * @param string $variable is case sensitive
709
   * @param string $value
710
   * @return array, see evaluate for return information.
711
   */
712
  function set_variable(string $variable, string $value): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
713
    $value = str_replace("\n", '\n', addslashes($value));
714
    return $this->evaluate("SET VARIABLE $variable \"$value\"");
715
  }
716
717
  /**
718
   * Play the given audio file, allowing playback to be interrupted by a DTMF digit. This command is similar to the GET DATA
719
   * command but this command returns after the first DTMF digit has been pressed while GET DATA can accumulated any number of
720
   * digits before returning.
721
   *
722
   * @param string $filename without extension, often in /var/lib/asterisk/sounds
723
   * @param string $escape_digits
724
   * @param integer $offset
725
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
726
   * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
727
   * @example examples/ping.php Ping an IP address
728
   *
729
   * @link http://www.voip-info.org/wiki-stream+file
730
   */
731
  function stream_file(string $filename, string $escape_digits = '', int $offset = 0): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
732
    return $this->evaluate("STREAM FILE $filename \"$escape_digits\" $offset");
733
  }
734
735
  /**
736
   * Enable or disable TDD transmission/reception on the current channel.
737
   *
738
   * @link http://www.voip-info.org/wiki-tdd+mode
739
   * @param string $setting can be on, off or mate
740
   * @return array, see evaluate for return information. ['result'] is 1 on success, 0 if the channel is not TDD capable.
741
   */
742
  function tdd_mode(string $setting): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
743
    return $this->evaluate("TDD MODE $setting");
744
  }
745
746
  /**
747
   * Sends $message to the Asterisk console via the 'verbose' message system.
748
   *
749
   * If the Asterisk verbosity level is $level or greater, send $message to the console.
750
   *
751
   * The Asterisk verbosity system works as follows. The Asterisk user gets to set the desired verbosity at startup time or later
752
   * using the console 'set verbose' command. Messages are displayed on the console if their verbose level is less than or equal
753
   * to desired verbosity set by the user. More important messages should have a low verbose level; less important messages
754
   * should have a high verbose level.
755
   *
756
   * @link http://www.voip-info.org/wiki-verbose
757
   * @param string $message
758
   * @param integer $level from 1 to 4
759
   * @return array, see evaluate for return information.
760
   */
761
  function verbose(string $message, int $level = 1): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
762
    $ret = [];
763
    foreach (explode("\n", str_replace("\r\n", "\n", print_r($message, true))) as $msg) {
764
      @syslog(LOG_WARNING, $msg);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for syslog(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

764
      /** @scrutinizer ignore-unhandled */ @syslog(LOG_WARNING, $msg);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
765
      $ret = $this->evaluate("VERBOSE \"$msg\" $level");
766
    }
767
    return $ret;
768
  }
769
770
  /**
771
   * Waits up to $timeout milliseconds for channel to receive a DTMF digit.
772
   *
773
   * @link http://www.voip-info.org/wiki-wait+for+digit
774
   * @param integer $timeout in milliseconds. Use -1 for the timeout value if you want the call to wait indefinitely.
775
   * @return array, see evaluate for return information. ['result'] is 0 if wait completes with no
776
   * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
777
   */
778
  function wait_for_digit(int $timeout = -1): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
779
    return $this->evaluate("WAIT FOR DIGIT $timeout");
780
  }
781
782
783
  // *********************************************************************************************************
784
  // **                             APPLICATIONS                                                                                        **
785
  // *********************************************************************************************************
786
787
  /**
788
   * Set absolute maximum time of call.
789
   *
790
   * Note that the timeout is set from the current time forward, not counting the number of seconds the call has already been up.
791
   * Each time you call AbsoluteTimeout(), all previous absolute timeouts are cancelled.
792
   * Will return the call to the T extension so that you can playback an explanatory note to the calling party (the called party
793
   * will not hear that)
794
   *
795
   * @link http://www.voip-info.org/wiki-Asterisk+-+documentation+of+application+commands
796
   * @link http://www.dynx.net/ASTERISK/AGI/ccard/agi-ccard.agi
797
   * @param integer $seconds allowed, 0 disables timeout
798
   * @return array, see evaluate for return information.
799
   */
800
  function exec_absolutetimeout(int $seconds = 0): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
801
    return $this->exec('AbsoluteTimeout', $seconds);
802
  }
803
804
  /**
805
   * Executes an AGI compliant application.
806
   *
807
   * @param string $command
808
   * @param string $args
809
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or if application requested hangup, or 0 on non-hangup exit.
810
   */
811
  function exec_agi(string $command, string $args): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
812
    return $this->exec("AGI $command", $args);
813
  }
814
815
  /**
816
   * Set Language.
817
   *
818
   * @param string $language code
819
   * @return array, see evaluate for return information.
820
   */
821
  function exec_setlanguage(string $language = 'en'): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
822
    return $this->exec('Set', 'CHANNEL(language)=' . $language);
823
  }
824
825
  /**
826
   * Do ENUM Lookup.
827
   *
828
   * Note: to retrieve the result, use
829
   *   get_variable('ENUM');
830
   *
831
   * @param $exten
832
   * @return array, see evaluate for return information.
833
   */
834
  function exec_enumlookup($exten): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
835
    return $this->exec('EnumLookup', $exten);
836
  }
837
838
  /**
839
   * Dial.
840
   *
841
   * Dial takes input from ${VXML_URL} to send XML Url to Cisco 7960
842
   * Dial takes input from ${ALERT_INFO} to set ring cadence for Cisco phones
843
   * Dial returns ${CAUSECODE}: If the dial failed, this is the errormessage.
844
   * Dial returns ${DIALSTATUS}: Text code returning status of last dial attempt.
845
   *
846
   * @link http://www.voip-info.org/wiki-Asterisk+cmd+Dial
847
   * @param string $type
848
   * @param string $identifier
849
   * @param integer $timeout
850
   * @param string $options
851
   * @param string $url
852
   * @return array, see evaluate for return information.
853
   */
854
  function exec_dial(string $type, string $identifier, $timeout = null, $options = null, $url = null): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
855
    return $this->exec(
856
      'Dial', trim("$type/$identifier" . $this->option_delim . $timeout . $this->option_delim . $options . $this->option_delim . $url, $this->option_delim));
857
  }
858
859
  /**
860
   * Goto.
861
   *
862
   * This function takes three arguments: context,extension, and priority, but the leading arguments
863
   * are optional, not the trailing arguments.  Those goto($z) sets the priority to $z.
864
   *
865
   * @param string $a
866
   * @param string $b ;
867
   * @param string $c ;
868
   * @return array, see evaluate for return information.
869
   */
870
  function exec_goto(string $a, $b = null, $c = null): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
871
    return $this->exec('Goto', trim($a . $this->option_delim . $b . $this->option_delim . $c, $this->option_delim));
872
  }
873
874
875
  // *********************************************************************************************************
876
  // **                             FAST PASSING                                                                                        **
877
  // *********************************************************************************************************
878
879
  /**
880
   * Say the given digit string, returning early if any of the given DTMF escape digits are received on the channel.
881
   * Return early if $buffer is adequate for request.
882
   *
883
   * @link http://www.voip-info.org/wiki-say+digits
884
   * @param string $buffer
885
   * @param integer $digits
886
   * @param string $escape_digits
887
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
888
   * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
889
   */
890
  function fastpass_say_digits(string &$buffer, int $digits, string $escape_digits = ''): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
891
    if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) {
892
      $res = $this->say_digits($digits, $escape_digits);
893
      $this->appendBuffer($buffer, $res);
894
      return $res;
895
    }
896
    return ['code' => AGI_Others::AGIRES_OK,
897
            'result' => $this->ordBuffer($buffer)];
898
  }
899
900
  /**
901
   * Say the given number, returning early if any of the given DTMF escape digits are received on the channel.
902
   * Return early if $buffer is adequate for request.
903
   *
904
   * @link http://www.voip-info.org/wiki-say+number
905
   * @param string $buffer
906
   * @param integer $number
907
   * @param string $escape_digits
908
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
909
   * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
910
   */
911
  function fastpass_say_number(string &$buffer, int $number, string $escape_digits = ''): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
912
    if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) {
913
      $res = $this->say_number($number, $escape_digits);
914
      $this->appendBuffer($buffer, $res);
915
      return $res;
916
    }
917
    return ['code' => AGI_Others::AGIRES_OK,
918
            'result' => $this->ordBuffer($buffer)];
919
  }
920
921
  /**
922
   * Say the given character string, returning early if any of the given DTMF escape digits are received on the channel.
923
   * Return early if $buffer is adequate for request.
924
   *
925
   * @link http://www.voip-info.org/wiki-say+phonetic
926
   * @param string $buffer
927
   * @param string $text
928
   * @param string $escape_digits
929
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
930
   * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
931
   */
932
  function fastpass_say_phonetic(string &$buffer, string $text, string $escape_digits = ''): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
933
    if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) {
934
      $res = $this->say_phonetic($text, $escape_digits);
935
      $this->appendBuffer($buffer, $res);
936
      return $res;
937
    }
938
    return ['code' => AGI_Others::AGIRES_OK,
939
            'result' => $this->ordBuffer($buffer)];
940
  }
941
942
  /**
943
   * Say a given time, returning early if any of the given DTMF escape digits are received on the channel.
944
   * Return early if $buffer is adequate for request.
945
   *
946
   * @link http://www.voip-info.org/wiki-say+time
947
   * @param string $buffer
948
   * @param integer $time number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC).
949
   * @param string $escape_digits
950
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
951
   * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
952
   */
953
  function fastpass_say_time(string &$buffer, $time = null, string $escape_digits = ''): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
954
    if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) {
955
      $res = $this->say_time($time, $escape_digits);
956
      $this->appendBuffer($buffer, $res);
957
      return $res;
958
    }
959
    return ['code' => AGI_Others::AGIRES_OK,
960
            'result' => $this->ordBuffer($buffer)];
961
  }
962
963
  /**
964
   * Play the given audio file, allowing playback to be interrupted by a DTMF digit. This command is similar to the GET DATA
965
   * command but this command returns after the first DTMF digit has been pressed while GET DATA can accumulated any number of
966
   * digits before returning.
967
   * Return early if $buffer is adequate for request.
968
   *
969
   * @link http://www.voip-info.org/wiki-stream+file
970
   * @param string $buffer
971
   * @param string $filename without extension, often in /var/lib/asterisk/sounds
972
   * @param string $escape_digits
973
   * @param integer $offset
974
   * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no
975
   * digit received, otherwise a decimal value of the DTMF tone.  Use chr() to convert to ASCII.
976
   */
977
  function fastpass_stream_file(string &$buffer, string $filename, string $escape_digits = '', int $offset = 0): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
978
    if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) {
979
      $res = $this->stream_file($filename, $escape_digits, $offset);
980
      $this->appendBuffer($buffer, $res);
981
      return $res;
982
    }
983
    return ['code' => AGI_Others::AGIRES_OK,
984
            'result' => $this->ordBuffer($buffer),
985
            'endpos' => 0];
986
  }
987
988
  /**
989
   * Use festival to read text.
990
   * Return early if $buffer is adequate for request.
991
   *
992
   * @link http://www.cstr.ed.ac.uk/projects/festival/
993
   * @param string $buffer
994
   * @param string $text
995
   * @param string $escape_digits
996
   * @param integer $frequency
997
   * @return array, see evaluate for return information.
998
   */
999
  function fastpass_text2wav(string &$buffer, string $text, string $escape_digits = '', int $frequency = 8000): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1000
    if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) {
1001
      $res = $this->text2wav($text, $escape_digits, $frequency);
1002
      $this->appendBuffer($buffer, $res);
1003
      return $res;
1004
    }
1005
    return ['code' => AGI_Others::AGIRES_OK,
1006
            'result' => $this->ordBuffer($buffer),
1007
            'endpos' => 0];
1008
  }
1009
1010
  /**
1011
   * Use Cepstral Swift to read text.
1012
   * Return early if $buffer is adequate for request.
1013
   *
1014
   * @link http://www.cepstral.com/
1015
   * @param string $buffer
1016
   * @param string $text
1017
   * @param string $escape_digits
1018
   * @param integer $frequency
1019
   * @param $voice
1020
   * @return array, see evaluate for return information.
1021
   */
1022
  function fastpass_swift(string &$buffer, string $text, string $escape_digits = '', int $frequency = 8000, $voice = null): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1023
    if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) {
1024
      $res = $this->swift($text, $escape_digits, $frequency, $voice);
1025
      $this->appendBuffer($buffer, $res);
1026
      return $res;
1027
    }
1028
    return ['code' => AGI_Others::AGIRES_OK,
1029
            'result' => $this->ordBuffer($buffer),
1030
            'endpos' => 0];
1031
  }
1032
1033
  /**
1034
   * Say Punctuation in a string.
1035
   * Return early if $buffer is adequate for request.
1036
   *
1037
   * @param string $buffer
1038
   * @param string $text
1039
   * @param string $escape_digits
1040
   * @param integer $frequency
1041
   * @return array, see evaluate for return information.
1042
   */
1043
  function fastpass_say_punctuation(string &$buffer, string $text, string $escape_digits = '', int $frequency = 8000): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1044
    if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) {
1045
      $res = $this->say_punctuation($text, $escape_digits, $frequency);
1046
      $this->appendBuffer($buffer, $res);
1047
      return $res;
1048
    }
1049
    return ['code' => AGI_Others::AGIRES_OK,
1050
            'result' => $this->ordBuffer($buffer)];
1051
  }
1052
1053
  /**
1054
   * Plays the given file and receives DTMF data.
1055
   * Return early if $buffer is adequate for request.
1056
   *
1057
   * This is similar to STREAM FILE, but this command can accept and return many DTMF digits,
1058
   * while STREAM FILE returns immediately after the first DTMF digit is detected.
1059
   *
1060
   * Asterisk looks for the file to play in /var/lib/asterisk/sounds by default.
1061
   *
1062
   * If the user doesn't press any keys when the message plays, there is $timeout milliseconds
1063
   * of silence then the command ends.
1064
   *
1065
   * The user has the opportunity to press a key at any time during the message or the
1066
   * post-message silence. If the user presses a key while the message is playing, the
1067
   * message stops playing. When the first key is pressed a timer starts counting for
1068
   * $timeout milliseconds. Every time the user presses another key the timer is restarted.
1069
   * The command ends when the counter goes to zero or the maximum number of digits is entered,
1070
   * whichever happens first.
1071
   *
1072
   * If you don't specify a time out then a default timeout of 2000 is used following a pressed
1073
   * digit. If no digits are pressed then 6 seconds of silence follow the message.
1074
   *
1075
   * If you don't specify $max_digits then the user can enter as many digits as they want.
1076
   *
1077
   * Pressing the # key has the same effect as the timer running out: the command ends and
1078
   * any previously keyed digits are returned. A side effect of this is that there is no
1079
   * way to read a # key using this command.
1080
   *
1081
   * @link http://www.voip-info.org/wiki-get+data
1082
   * @param string $buffer
1083
   * @param string $filename file to play. Do not include file extension.
1084
   * @param integer $timeout milliseconds
1085
   * @param integer $max_digits
1086
   * @return array, see evaluate for return information. ['result'] holds the digits and ['data'] holds the timeout if present.
1087
   *
1088
   * This differs from other commands with return DTMF as numbers representing ASCII characters.
1089
   */
1090
  function fastpass_get_data(string &$buffer, string $filename, $timeout = null, $max_digits = null): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1091
    if (is_null($max_digits) || strlen($buffer) < $max_digits) {
1092
      if ($buffer == '') {
1093
        $res = $this->get_data($filename, $timeout, $max_digits);
1094
        if ($res['code'] == AGI_Others::AGIRES_OK) {
1095
          $buffer .= $res['result'];
1096
        }
1097
        return $res;
1098
      }
1099
      while (is_null($max_digits) || strlen($buffer) < $max_digits) {
1100
        $res = $this->wait_for_digit();
1101
        if ($res['code'] != AGI_Others::AGIRES_OK) {
1102
          return $res;
1103
        }
1104
        if ($res['result'] == ord('#')) {
1105
          break;
1106
        }
1107
        $buffer .= chr($res['result']);
1108
      }
1109
    }
1110
    return ['code' => AGI_Others::AGIRES_OK,
1111
            'result' => $buffer];
1112
  }
1113
1114
  // *********************************************************************************************************
1115
  // **                             DERIVED                                                                                             **
1116
  // *********************************************************************************************************
1117
1118
  /**
1119
   * Menu.
1120
   *
1121
   * This function presents the user with a menu and reads the response
1122
   *
1123
   * @param array $choices has the following structure:
1124
   *   array('1'=>'*Press 1 for this', // festival reads if prompt starts with *
1125
   *           '2'=>'some-gsm-without-extension',
1126
   *           '*'=>'*Press star for help');
1127
   * @param int $timeout
1128
   * @return mixed key pressed on success, -1 on failure
1129
   */
1130
  function menu(array $choices, int $timeout = 2000) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1131
    $keys = join('', array_keys($choices));
1132
    $choice = null;
1133
    while (is_null($choice)) {
1134
      foreach ($choices as $prompt) {
1135
        if ($prompt[0] == '*') {
1136
          $ret = $this->text2wav(substr($prompt, 1), $keys);
1137
        } else {
1138
          $ret = $this->stream_file($prompt, $keys);
1139
        }
1140
1141
        if ($ret['code'] != AGI_Others::AGIRES_OK || $ret['result'] == -1) {
1142
          $choice = -1;
1143
          break;
1144
        }
1145
1146
        if ($ret['result'] != 0) {
1147
          $choice = chr($ret['result']);
1148
          break;
1149
        }
1150
      }
1151
1152
      if (is_null($choice)) {
1153
        $ret = $this->get_data('beep', $timeout, 1);
1154
        if ($ret['code'] != AGI_Others::AGIRES_OK || $ret['result'] == -1) {
1155
          $choice = -1;
1156
        } elseif ($ret['result'] != '' && strpos(' ' . $keys, $ret['result'])) {
1157
          $choice = $ret['result'];
1158
        }
1159
      }
1160
    }
1161
    return $choice;
1162
  }
1163
1164
  /**
1165
   * setContext - Set context, extension and priority.
1166
   *
1167
   * @param string $context
1168
   * @param string $extension
1169
   * @param int $priority
1170
   */
1171
  function setContext(string $context, string $extension = 's', int $priority = 1) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1172
    $this->set_context($context);
1173
    $this->set_extension($extension);
1174
    $this->set_priority($priority);
1175
  }
1176
1177
  /**
1178
   * Parse caller id.
1179
   *
1180
   * @param string $callerid
1181
   * @return array('Name'=>$name, 'Number'=>$number)
1182
   * @example examples/dtmf.php Get DTMF tones from the user and say the digits
1183
   * @example examples/input.php Get text input from the user and say it back
1184
   *
1185
   * "name" <proto:user@server:port>
1186
   *
1187
   */
1188
  function parse_callerid($callerid = null): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1189
    if (is_null($callerid)) {
1190
      $callerid = $this->request['agi_callerid'];
1191
    }
1192
1193
    $ret = ['name' => '',
1194
            'protocol' => '',
1195
            'username' => '',
1196
            'host' => '',
1197
            'port' => ''];
1198
    $callerid = trim($callerid);
1199
1200
    if ($callerid[0] == '"' || $callerid[0] == "'") {
1201
      $d = $callerid[0];
1202
      $callerid = explode($d, substr($callerid, 1));
1203
      $ret['name'] = array_shift($callerid);
1204
      $callerid = join($d, $callerid);
1205
    }
1206
1207
    $callerid = explode('@', trim($callerid, '<> '));
1208
    $username = explode(':', array_shift($callerid));
1209
    if (count($username) == 1) {
1210
      $ret['username'] = $username[0];
1211
    } else {
1212
      $ret['protocol'] = array_shift($username);
1213
      $ret['username'] = join(':', $username);
1214
    }
1215
1216
    $callerid = join('@', $callerid);
1217
    $host = explode(':', $callerid);
1218
    if (count($host) == 1) {
1219
      $ret['host'] = $host[0];
1220
    } else {
1221
      $ret['host'] = array_shift($host);
1222
      $ret['port'] = join(':', $host);
1223
    }
1224
1225
    return $ret;
1226
  }
1227
1228
  /**
1229
   * Use festival to read text.
1230
   *
1231
   * @param string $text
1232
   * @param string $escape_digits
1233
   * @param integer $frequency
1234
   * @return array | bool, see evaluate for return information.
1235
   * @example examples/dtmf.php Get DTMF tones from the user and say the digits
1236
   * @example examples/input.php Get text input from the user and say it back
1237
   * @example examples/ping.php Ping an IP address
1238
   *
1239
   * @link http://www.cstr.ed.ac.uk/projects/festival/
1240
   */
1241
  function text2wav(string $text, string $escape_digits = '', int $frequency = 8000) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1242
    $text = trim($text);
1243
    if ($text == '') {
1244
      return true;
1245
    }
1246
1247
    $hash = md5($text);
1248
    $fname = $this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR;
1249
    $fname .= 'text2wav_' . $hash;
1250
1251
    // create wave file
1252
    if (!file_exists("$fname.wav")) {
1253
      // write text file
1254
      if (!file_exists("$fname.txt")) {
1255
        $fp = fopen("$fname.txt", 'w');
1256
        fputs($fp, $text);
1257
        fclose($fp);
1258
      }
1259
1260
      shell_exec("{$this->config['festival']['text2wave']} -F $frequency -o $fname.wav $fname.txt");
1261
    } else {
1262
      touch("$fname.txt");
1263
      touch("$fname.wav");
1264
    }
1265
1266
    // stream it
1267
    $ret = $this->stream_file($fname, $escape_digits);
1268
1269
    // clean up old files
1270
    $delete = time() - 2592000; // 1 month
1271
    foreach (glob($this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR . 'text2wav_*') as $file) {
1272
      if (filemtime($file) < $delete) {
1273
        unlink($file);
1274
      }
1275
    }
1276
1277
    return $ret;
1278
  }
1279
1280
  /**
1281
   * Use Cepstral Swift to read text.
1282
   *
1283
   * @link http://www.cepstral.com/
1284
   * @param string $text
1285
   * @param string $escape_digits
1286
   * @param integer $frequency
1287
   * @param $voice
1288
   * @return array | bool, see evaluate for return information.
1289
   */
1290
  function swift(string $text, string $escape_digits = '', int $frequency = 8000, $voice = null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1291
    if (!is_null($voice)) {
1292
      $voice = "-n $voice";
1293
    } elseif (isset($this->config['cepstral']['voice'])) {
1294
      $voice = "-n {$this->config['cepstral']['voice']}";
1295
    }
1296
1297
    $text = trim($text);
1298
    if ($text == '') {
1299
      return true;
1300
    }
1301
1302
    $hash = md5($text);
1303
    $fname = $this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR;
1304
    $fname .= 'swift_' . $hash;
1305
1306
    // create wave file
1307
    if (!file_exists("$fname.wav")) {
1308
      // write text file
1309
      if (!file_exists("$fname.txt")) {
1310
        $fp = fopen("$fname.txt", 'w');
1311
        fputs($fp, $text);
1312
        fclose($fp);
1313
      }
1314
1315
      shell_exec("{$this->config['cepstral']['swift']} -p audio/channels=1,audio/sampling-rate=$frequency $voice -o $fname.wav -f $fname.txt");
1316
    }
1317
1318
    // stream it
1319
    $ret = $this->stream_file($fname, $escape_digits);
1320
1321
    // clean up old files
1322
    $delete = time() - 2592000; // 1 month
1323
    foreach (glob($this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR . 'swift_*') as $file) {
1324
      if (filemtime($file) < $delete) {
1325
        unlink($file);
1326
      }
1327
    }
1328
1329
    return $ret;
1330
  }
1331
1332
  /**
1333
   * Text Input.
1334
   *
1335
   * Based on ideas found at http://www.voip-info.org/wiki-Asterisk+cmd+DTMFToText
1336
   *
1337
   * Example:
1338
   *                  UC   H     LC   i        ,     SP   h     o        w    SP   a    r        e     SP   y        o        u     ?
1339
   *   $string = '*8'.'44*'.'*5'.'444*'.'00*'.'0*'.'44*'.'666*'.'9*'.'0*'.'2*'.'777*'.'33*'.'0*'.'999*'.'666*'.'88*'.'0000*';
1340
   *
1341
   * @link http://www.voip-info.org/wiki-Asterisk+cmd+DTMFToText
1342
   * @example examples/input.php Get text input from the user and say it back
1343
   *
1344
   * @param string $mode
1345
   * @return string
1346
   */
1347
  function text_input(string $mode = 'NUMERIC'): string {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1348
    $alpha = ['k0' => ' ',
1349
              'k00' => ',',
1350
              'k000' => '.',
1351
              'k0000' => '?',
1352
              'k00000' => '0',
1353
              'k1' => '!',
1354
              'k11' => ':',
1355
              'k111' => ';',
1356
              'k1111' => '#',
1357
              'k11111' => '1',
1358
              'k2' => 'A',
1359
              'k22' => 'B',
1360
              'k222' => 'C',
1361
              'k2222' => '2',
1362
              'k3' => 'D',
1363
              'k33' => 'E',
1364
              'k333' => 'F',
1365
              'k3333' => '3',
1366
              'k4' => 'G',
1367
              'k44' => 'H',
1368
              'k444' => 'I',
1369
              'k4444' => '4',
1370
              'k5' => 'J',
1371
              'k55' => 'K',
1372
              'k555' => 'L',
1373
              'k5555' => '5',
1374
              'k6' => 'M',
1375
              'k66' => 'N',
1376
              'k666' => 'O',
1377
              'k6666' => '6',
1378
              'k7' => 'P',
1379
              'k77' => 'Q',
1380
              'k777' => 'R',
1381
              'k7777' => 'S',
1382
              'k77777' => '7',
1383
              'k8' => 'T',
1384
              'k88' => 'U',
1385
              'k888' => 'V',
1386
              'k8888' => '8',
1387
              'k9' => 'W',
1388
              'k99' => 'X',
1389
              'k999' => 'Y',
1390
              'k9999' => 'Z',
1391
              'k99999' => '9'];
1392
    $symbol = ['k0' => '=',
1393
               'k1' => '<',
1394
               'k11' => '(',
1395
               'k111' => '[',
1396
               'k1111' => '{',
1397
               'k11111' => '1',
1398
               'k2' => '@',
1399
               'k22' => '$',
1400
               'k222' => '&',
1401
               'k2222' => '%',
1402
               'k22222' => '2',
1403
               'k3' => '>',
1404
               'k33' => ')',
1405
               'k333' => ']',
1406
               'k3333' => '}',
1407
               'k33333' => '3',
1408
               'k4' => '+',
1409
               'k44' => '-',
1410
               'k444' => '*',
1411
               'k4444' => '/',
1412
               'k44444' => '4',
1413
               'k5' => "'",
1414
               'k55' => '`',
1415
               'k555' => '5',
1416
               'k6' => '"',
1417
               'k66' => '6',
1418
               'k7' => '^',
1419
               'k77' => '7',
1420
               'k8' => "\\",
1421
               'k88' => '|',
1422
               'k888' => '8',
1423
               'k9' => '_',
1424
               'k99' => '~',
1425
               'k999' => '9'];
1426
    $text = '';
1427
    do {
1428
      $command = false;
1429
      $result = $this->get_data('beep');
1430
      foreach (explode('*', $result['result']) as $code) {
1431
        if ($command) {
1432
          switch ($code[0]) {
1433
            case '2':
1434
              $text = substr($text, 0, strlen($text) - 1);
1435
              break; // backspace
1436
            case '5':
1437
              $mode = 'LOWERCASE';
1438
              break;
1439
            case '6':
1440
              $mode = 'NUMERIC';
1441
              break;
1442
            case '7':
1443
              $mode = 'SYMBOL';
1444
              break;
1445
            case '8':
1446
              $mode = 'UPPERCASE';
1447
              break;
1448
            case '9':
1449
              $text = explode(' ', $text);
1450
              unset($text[count($text) - 1]);
1451
              $text = join(' ', $text);
1452
              break; // backspace a word
1453
          }
1454
          $code = substr($code, 1);
1455
          $command = false;
1456
        }
1457
        if ($code == '') {
1458
          $command = true;
1459
        } elseif ($mode == 'NUMERIC') {
1460
          $text .= $code;
1461
        } elseif ($mode == 'UPPERCASE' && isset($alpha['k' . $code])) {
1462
          $text .= $alpha['k' . $code];
1463
        } elseif ($mode == 'LOWERCASE' && isset($alpha['k' . $code])) {
1464
          $text .= strtolower($alpha['k' . $code]);
1465
        } elseif ($mode == 'SYMBOL' && isset($symbol['k' . $code])) {
1466
          $text .= $symbol['k' . $code];
1467
        }
1468
      }
1469
      $this->say_punctuation($text);
1470
    } while (substr($result['result'], -2) == '**');
1471
    return $text;
1472
  }
1473
1474
  /**
1475
   * Say Punctuation in a string.
1476
   *
1477
   * @param string $text
1478
   * @param string $escape_digits
1479
   * @param integer $frequency
1480
   * @return array, see evaluate for return information.
1481
   */
1482
  function say_punctuation(string $text, string $escape_digits = '', int $frequency = 8000): array {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1483
    $ret = "";
1484
    for ($i = 0; $i < strlen($text); $i++) {
1485
      switch ($text[$i]) {
1486
        case ' ':
1487
          $ret .= 'SPACE ';
1488
          break;
1489
        case ',':
1490
          $ret .= 'COMMA ';
1491
          break;
1492
        case '.':
1493
          $ret .= 'PERIOD ';
1494
          break;
1495
        case '?':
1496
          $ret .= 'QUESTION MARK ';
1497
          break;
1498
        case '!':
1499
          $ret .= 'EXPLANATION POINT ';
1500
          break;
1501
        case ':':
1502
          $ret .= 'COLON ';
1503
          break;
1504
        case ';':
1505
          $ret .= 'SEMICOLON ';
1506
          break;
1507
        case '#':
1508
          $ret .= 'POUND ';
1509
          break;
1510
        case '=':
1511
          $ret .= 'EQUALS ';
1512
          break;
1513
        case '<':
1514
          $ret .= 'LESS THAN ';
1515
          break;
1516
        case '(':
1517
          $ret .= 'LEFT PARENTHESIS ';
1518
          break;
1519
        case '[':
1520
          $ret .= 'LEFT BRACKET ';
1521
          break;
1522
        case '{':
1523
          $ret .= 'LEFT BRACE ';
1524
          break;
1525
        case '@':
1526
          $ret .= 'AT ';
1527
          break;
1528
        case '$':
1529
          $ret .= 'DOLLAR SIGN ';
1530
          break;
1531
        case '&':
1532
          $ret .= 'AMPERSAND ';
1533
          break;
1534
        case '%':
1535
          $ret .= 'PERCENT ';
1536
          break;
1537
        case '>':
1538
          $ret .= 'GREATER THAN ';
1539
          break;
1540
        case ')':
1541
          $ret .= 'RIGHT PARENTHESIS ';
1542
          break;
1543
        case ']':
1544
          $ret .= 'RIGHT BRACKET ';
1545
          break;
1546
        case '}':
1547
          $ret .= 'RIGHT BRACE ';
1548
          break;
1549
        case '+':
1550
          $ret .= 'PLUS ';
1551
          break;
1552
        case '-':
1553
          $ret .= 'MINUS ';
1554
          break;
1555
        case '*':
1556
          $ret .= 'ASTERISK ';
1557
          break;
1558
        case '/':
1559
          $ret .= 'SLASH ';
1560
          break;
1561
        case "'":
1562
          $ret .= 'SINGLE QUOTE ';
1563
          break;
1564
        case '`':
1565
          $ret .= 'BACK TICK ';
1566
          break;
1567
        case '"':
1568
          $ret .= 'QUOTE ';
1569
          break;
1570
        case '^':
1571
          $ret .= 'CAROT ';
1572
          break;
1573
        case "\\":
1574
          $ret .= 'BACK SLASH ';
1575
          break;
1576
        case '|':
1577
          $ret .= 'BAR ';
1578
          break;
1579
        case '_':
1580
          $ret .= 'UNDERSCORE ';
1581
          break;
1582
        case '~':
1583
          $ret .= 'TILDE ';
1584
          break;
1585
        default:
1586
          $ret .= $text[$i] . ' ';
1587
          break;
1588
      }
1589
    }
1590
    return $this->text2wav($ret, $escape_digits, $frequency);
1591
  }
1592
1593
  /**
1594
   * Create a new AGI_AsteriskManager.
1595
   */
1596
  function &new_AsteriskManager(): AGI_AsteriskManager {
1597
    $this->asm = new AGI_AsteriskManager(null, $this->config['asmanager']);
1598
    $this->asm->setPagi($this);
1599
    $this->config['asmanager'] = &$this->asm->config['asmanager'];
1600
    return $this->asm;
1601
  }
1602
1603
1604
  // *********************************************************************************************************
1605
  // **                             PRIVATE                                                                                             **
1606
  // *********************************************************************************************************
1607
1608
1609
  /**
1610
   * Evaluate an AGI command.
1611
   *
1612
   * @access private
1613
   * @param string $command
1614
   * @return array ('code'=>$code, 'result'=>$result, 'data'=>$data)
1615
   */
1616
  private function evaluate(string $command): array {
1617
    $broken = ['code' => 500,
1618
               'result' => -1,
1619
               'data' => ''];
1620
1621
    // write command
1622
    if (!@fwrite($this->out, trim($command) . "\n")) {
1623
      return $broken;
1624
    }
1625
    fflush($this->out);
1626
1627
    // Read result.  Occasionally, a command return a string followed by an extra new line.
1628
    // When this happens, our script will ignore the new line, but it will still be in the
1629
    // buffer.  So, if we get a blank line, it is probably the result of a previous
1630
    // command.  We read until we get a valid result or asterisk hangs up.  One offending
1631
    // command is SEND TEXT.
1632
    $count = 0;
1633
    do {
1634
      $str = trim(fgets($this->in, 4096));
1635
    } while ($str == '' && $count++ < 5);
1636
1637
    if ($count >= 5) {
1638
      //          $this->conlog("evaluate error on read for $command");
1639
      return $broken;
1640
    }
1641
1642
    // parse result
1643
    $ret['code'] = substr($str, 0, 3);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$ret was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ret = array(); before regardless.
Loading history...
1644
    $str = trim(substr($str, 3));
1645
1646
    if ($str[0] == '-') { // we have a multiline response!
1647
      $count = 0;
1648
      $str = substr($str, 1) . "\n";
1649
      $line = fgets($this->in, 4096);
1650
      while (substr($line, 0, 3) != $ret['code'] && $count < 5) {
1651
        $str .= $line;
1652
        $line = fgets($this->in, 4096);
1653
        $count = (trim($line) == '') ? $count + 1 : 0;
1654
      }
1655
      if ($count >= 5) {
1656
        //            $this->conlog("evaluate error on multiline read for $command");
1657
        return $broken;
1658
      }
1659
    }
1660
1661
    $ret['result'] = null;
1662
    $ret['data'] = '';
1663
    if ($ret['code'] != AGI_Others::AGIRES_OK) { // some sort of error
1664
      $ret['data'] = $str;
1665
      $this->conlog(/** @scrutinizer ignore-type */ print_r($ret, true));
1666
    } else { // normal AGIRES_OK response
1667
      $parse = explode(' ', trim($str));
1668
      $in_token = false;
1669
      foreach ($parse as $token) {
1670
        if ($in_token) { // we previously hit a token starting with ')' but not ending in ')'
1671
          $ret['data'] .= ' ' . trim($token, '() ');
1672
          if ($token[strlen($token) - 1] == ')') {
1673
            $in_token = false;
1674
          }
1675
        } elseif ($token[0] == '(') {
1676
          if ($token[strlen($token) - 1] != ')') {
1677
            $in_token = true;
1678
          }
1679
          $ret['data'] .= ' ' . trim($token, '() ');
1680
        } elseif (strpos($token, '=')) {
1681
          $token = explode('=', $token);
1682
          $ret[$token[0]] = $token[1];
1683
        } elseif ($token != '') {
1684
          $ret['data'] .= ' ' . $token;
1685
        }
1686
      }
1687
      $ret['data'] = trim($ret['data']);
1688
    }
1689
1690
    // log some errors
1691
    if ($ret['result'] < 0) {
1692
      $this->conlog("$command returned {$ret['result']}");
1693
    }
1694
1695
    return $ret;
1696
  }
1697
1698
  /**
1699
   * Log to console if debug mode.
1700
   *
1701
   * @param string $str
1702
   * @param integer $vbl verbose level
1703
   * @example examples/ping.php Ping an IP address
1704
   *
1705
   */
1706
  function conlog(string $str, int $vbl = 1) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1707
    static $busy = false;
1708
1709
    if ($this->config['phpagi']['debug'] != false) {
1710
      if (!$busy) { // no conlogs inside conlog!!!
1711
        $busy = true;
1712
        $this->verbose($str, $vbl);
1713
        $busy = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $busy is dead and can be removed.
Loading history...
1714
      }
1715
    }
1716
  }
1717
1718
  /**
1719
   * Find an executable in the path.
1720
   *
1721
   * @access private
1722
   * @param string $cmd command to find
1723
   * @param string $checkpath path to check
1724
   * @return string the path to the command
1725
   */
1726
  private function which(string $cmd, $checkpath = null) {
1727
    if (is_null($checkpath)) {
1728
      $chpath = getenv('PATH');
1729
      if ($chpath === false) {
1730
        $chpath = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:' .
1731
          '/usr/X11R6/bin:/usr/local/apache/bin:/usr/local/mysql/bin';
1732
      }
1733
    } else {
1734
      $chpath = $checkpath;
1735
    }
1736
1737
    foreach (explode(':', $chpath) as $path) {
1738
      if (is_executable("$path/$cmd")) {
1739
        return "$path/$cmd";
1740
      }
1741
    }
1742
1743
    return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
1744
  }
1745
1746
  /**
1747
   * Make a folder recursively.
1748
   *
1749
   * @access private
1750
   * @param string $folder
1751
   * @param integer $perms
1752
   * @return boolean
1753
   */
1754
  private function make_folder(string $folder, int $perms = 0755): bool {
1755
    $f = explode(DIRECTORY_SEPARATOR, $folder);
1756
    $base = '';
1757
    for ($i = 0; $i < count($f); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1758
      $base .= $f[$i];
1759
      if ($f[$i] != '' && !file_exists($base)) {
1760
        if (mkdir($base, $perms) == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1761
          return (false);
1762
        }
1763
      }
1764
      $base .= DIRECTORY_SEPARATOR;
1765
    }
1766
    return (true);
1767
  }
1768
1769
  private function proceedDigits(string $buffer, string $escapeDigits): bool {
1770
    if ($escapeDigits != "" && $buffer != "") {
1771
      if (!strpos(chr(255) . $escapeDigits, $buffer[strlen($buffer) - 1])) {
1772
        return true;
1773
      }
1774
    }
1775
    return false;
1776
  }
1777
1778
  private function ordBuffer(string $buffer): int {
1779
    return ord($buffer[strlen($buffer) - 1]);
1780
  }
1781
1782
  private function appendBuffer(string &$buffer, array $res) {
1783
    if ($res['code'] == AGI_Others::AGIRES_OK && $res['result'] > 0) {
1784
      $buffer .= chr($res['result']);
1785
    }
1786
  }
1787
}
1788
1789