PAGI: Quick telephony applications using AGI and PHPTweet
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:
- Example application: Complete IVR application skeleton, including unit tests.
- http://marcelog.github.com/PAGI/: The homepage.
- http://packagist.org/packages/marcelog/pagi: Packagist home
- API documentation
- https://github.com/marcelog/PAGI/: The GitHub repository
- Full PAGI examples
- AGI Protocol article
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.
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
- $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:
- $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.
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
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
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.
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
Start writing your VoIP Telephony Applications in PHP for Asterisk
Enough writing already, right? Go make your own ivr applications now :)