Articles

PAGI: Quick telephony applications using AGI and PHP


The framework to create IVR and VoIP Applications in PHP for the Asterisk PBX

PAGI (not to be confused with phpagi) is a PHP 5.3 client for AGI: Asterisk Gateway Interface. AGI is a very simple protocol, and it has been implemented in a wide variety of languages, allowing to quickly create telephony applications, like IVR's, voicemails, prepaid telephony systems, etc. If you want or need to create this kind of telephony applications using PHP, you will find PAGI very useful.

UPDATE: Check the slides about writing telephony applications using Asterisk, PHP, and PAGI and PAMI, at the PHP Conference Argentina 2013. You can download the slides, don't forget to see the notes! The complete talk is right there if you missed it :)

By the way, it turns out this is quite a long article, but just because I wanted to enumerate and get an overview of all the features (or almost) that pagi offers, you can really skip the parts you are not interested in, it's kind of a "modular" reading.

NOTE: Since version 1.11.0 PAGI comes with call flow nodes, which are *very* useful when creating ivr applications without having to manually use the PAGI client. You can find out more in "Advanced telephony applications with PHP and PAGI using call flow nodes".

Getting PAGI for PHP

First, a couple of useful links:

Installing PAGI for PHP

PAGI is available via composer, please see the repo README file for instructions on how to use it.

Setup asterisk dialplan

First of all, you've got to setup asterisk so it will run your agi application in a given extension. For example, we can do this in the extensions.conf file:

The _X. is a dialplan pattern, it will match any number dialed as long as it is 1 digit long. You can of course go ahead and write your own pattern or use an exact number, like "111" or "5555555".

So the first line uses the dialplan AGI command to invoke your application. This tells asterisk to fork() a new process and exec() the given path (in this case /tmp/app.php, but it can be anything that is executable, we'll see that in a bit).

The second line is just a safe bet, a Hangup command, just in case your app goes a little funny on the call and does not terminate it in an appropiate way.

Create an AGI application file for the Asterisk PBX

Of course this file is just an entry point, and in this particular case, also the application. But as any other entry point, it can be used to just bootstrap your real application, in the case you have a more complex scenario.

So, assuming your php binary is located in "/usr/bin/php" create the file /tmp/app.php with this content:

The first line is the shebang line that will make /usr/bin/php the interpreter for the file, effectively using php to run our application.

In order for the operating system to execute file, make it executable:

NOTE: You should only give execute permissions to the asterisk user instead of just making it executable for all users, this was just an example.

If you want to have a little better structure for the project (with configurable php installation and php.ini files), or just dont like to hardcode the php interpreter there (good for you!), you might want to take a look at "Creating isolated environments for PHP applications with PEAR dependencies".

The PAGI Client

The pagi client is the central player here, it will handle all the communication with asterisk during the call. Actually, as you can see, this is just an interface. An abstract implementation exists (the AbstractClient) that implements the AGI commands declared in the IClient interface, *but* it leaves the implementation details for managing the I/O to its subclasses.

Specifically, this lets you use the standard client implementation, or something like pami's async agi client, suitable for async agi applications. Read more about PAMI for PHP.

In this case, we're going to use the standard pagi client implementation.

Getting an instance of the client

This is as easy as:

NOTE: The client accepts an array with options. The available options are:

  • stdin: Optional. If set, should contain an already open stream from where the client will read data (useful to make it interact with fastagi servers or even text files to mock stuff when testing). If not set, stdin will be used by the client
  • stdout: Same as stdin but for the output of the client

NOTE: AGI establishes the use of stdin and stdout for communication, you cant write anything that outputs to console directly (like echo's, or var_dump's). That's why you can set your own logger in the client like this:

The logger must be a PSR-3 compatible logger.

Checkpoint #1: A basic VoIP application for Asterisk

Now dial into your asterisk, you should listen to the numbers "one", "two", and "three". From now on, I'll mention some if the many things you can do through the pagi client, you should refer to the IClient interface to see all the available stuff!

The decorated Results

Sometimes you want the user to input 1 digit, or many digits, or let him/her interrupt the messages played, or do something if no input is received, or any kind of logic associated with playing and reading simultaneously. Every result from an agi command that pagi issues, is returned as an IResult. This is then implemented in ResultDecorator, effectively implementing the decorator pattern to return the result of the operation. This is so, because sometimes you only want to play a file, sometimes you just want to read digits, and sometimes you want to play AND read input from the user, and you might need to check the result of one or all the operations. See the API documentation for all the results available, in the namespace PAGI\Client\Result. You should check the api documentation for the method you are using to see what kind of result is available (ExecResult, RecordResult, FaxResult, PlayResult, etc). Do not be freighten! It's quite easy to use them. See below.

Playing a sound file in a Call from your PHP Application in Asterisk

Where:

  • $aSoundFile: A sound file to play, like 'welcome', or 'silence/1', etc
  • $escapeDigits: The digits that can be used to skip the sound file, like "#" or "01234567890*#".

$result will be a PlayResult that you can use to get information about what happened while the file was playing.

Reading input from the user from your PHP Application in Asterisk

To read input from the user, you have a couple of options:

Where:

  • $maxInputTime: The maximum amount of time to wait for the user input in milliseconds.
  • $maxDigitsToRead: The maximum number of digits a user can input for this reading.

In the case of waitDigit(), the result is a DigitReadResult, and the others return a PlayResult.

Playing indication tones of the Call from your PHP Application in Asterisk

You can also play some standard tones, these might need some tweaking in your indications.conf file:

And you can also send indications at the signaling level, without playing any audio:

Playing Music On Hold in a Call

Putting music on hold is also quite easy. You might need to configure your moh.conf file:

Logging through asterisk

In case you want to log stuff through the asterisk logger (so your messages get to the asterisk console or general asterisk log files), you can use the AsteriskLogger interface, remember that when logging through asterisk, you might need to tweak your logger.conf file:

Manipulating the channel variables

When AGI handshake starts, the server (asterisk) sends a couple of channel variables. The pagi client offers the IChannelVariables interface to access them:

To set a variable, you would do:

The IChannelVariables also provides access to some important environment variables. For example, to get the directory where spooled call files should go:

Manipulating the CDR (Call Detail Record)

Pagi offers the ICDR interface to interact with the cdr's, by setting custom values and retrieving the ones set by asterisk itself:

Working with the Caller ID's

In order to get and set caller id values, you can access the ICallerId interface:

In this case, the next dial command will carry the caller id set.

To set the caller id presentation mode:

For the complete list of caller id presentation modes, see: http://www.voip-info.org/wiki/view/Asterisk+func+CALLERPRES

Manipulating SIP headers

Before issuing a dial(), you may need to set some sip headers (to set privacy settings, or whatever). Here's how you can do it with the pagi client:

To delete a header:

Spooling calls through call files

Asterisk lets you use call files to generate calls automatically, you can access this feature via the ICallSpool interface:

The dial descriptor is an abstraction over dial strings, so you dont have to code them yourself. Currently, PAGI supports SIPDialDescriptor and DAHDIDialDescriptor.

This example will then issue a dial to SIP/myDevice@myProvider with the caller id "123". When the call is answered, it will go to context "campaignContext", in the priority 1 of the extension 555. There are many options you can use in the CallFile, like custom variables, timeouts, etc.

The array passed to the CallSpoolImpl::getInstance() method is necessary so the call file can be created in a temporary directory and then moved to the real asterisk spool directory, so asterisk will pick it up when it's completely written to disk.

Dialing

If you want to Dial from an agi application:

The result is a DialResult

Last but not least: The PAGI Application (suitable to rapidly create your telephony applications for Asterisk)

The pagi client can also be used inside a PAGI Application. That is, your own ivr application can be itself a PAGIApplication, by extending it. This will provide a number of features:

  • An error handler is automatically registered, and will invoke PAGIApplication::errorHandler() method.
  • A signal handler is automatically registered for the signals SIGINT, SIGQUIT, SIGTERM, SIGHUP, SIGUSR1, SIGUSR2, SIGCHLD, SIGALRM, and will invoke PAGIApplication::signalHandler() method.
  • A shutdown handler is automatically registered, and will invoke PAGIApplication::shutdown()
  • An init method, PAGIApplication::init()
  • A PSR-3 logger instance in PAGIApplication::$logger (protected).

Checkpoint #2: A complete PAGI Application Skeleton Example

See a complete example of an ivr application written in PHP with PAGI for the Asterisk PBX, including unit tests.

Start writing your VoIP Telephony Applications in PHP for Asterisk

Enough writing already, right? Go make your own ivr applications now :)