Articles

Create VoIP applications for Asterisk using PHP, PAMI, and Ding


Writing VoIP applications in PHP for Asterisk using PAMI

NOTE: If you are just looking for an introduction to ami and PAMI, take a look at: "Getting Started with the PAMI: PHP Asterisk Manager Interface = Easy Asterisk Monitoring".

Here, I'll write about AsterTrace (https://github.com/marcelog/AsterTrace), a simple project that will help you get started using the asterisk manager interface in extremely few lines of code :)

The goal for AsterTrace is to log into ami and capture every event sent by asterisk, process some (or not) and maybe log every one of them to some database.

AsterTrace uses Ding and PAMI. The first one as the container, and latter to communicate with AMI, so I'll talk about how AsterTrace uses them both, and how this will help you here and in other applications to get a more decoupled code, very easy to mantain and scale.

A similar article with Ding and Doctrine2 as ORM is available in "Writing PHP applications with Doctrine2 as ORM and Ding as DI container".

Installing AsterTrace, the horsepower of your telephony application

You can get AsterTrace directly from github:

  • ZIP
  • TAR
  • SVN: http://svn.github.com/marcelog/AsterTrace.git
  • GIT: git://github.com/marcelog/AsterTrace.git

That's it :) Now read on to see how it works.

How AsterTrace makes possible to rapidly write VoIP and Telephony Applications

To sum up, this is the general idea:

  • Use Ding as Inversion of Control and Dependency Injection container. All we're going to do are just beans, plain old php objects.
  • Use PAMI as the framework to handle the connection to the Asterisk Manager Interface.
  • Use Ding's PamiHelper so we dont need to deal with pami use details. Our main program will be just a bean.
  • Use Ding's PropertiesHolder so we can configure mysql, ami, php, etc, from properties file (aka php INI files).
  • Use Ding's TCPServerHelper to open a tcp server where you can connect via telnet or via the language of your choice, to listen for events serialized using json. You can also send commands and get the responses in json format.
  • Our main program (called from PamiHelper) will use Ding's events to dispatch events coming in from AMI, to our "event listeners".
  • A REST interface is also provided, so this software can be used from a web environment.
  • Every event listener will be just a bean, that will get called by the container whenever an event is dispatched from our main program.
  • We are going to log events to a database. I chose to use mysql, via pdo. This PDO object is a bean.
  • To actually write to the database, we'll use prepared statements.
  • Each prepared statement will be a "bean", and will get injected to our event listeners. These beans are instantiated by ding using the factory-method and factory-bean options.
  • We need to log to files the normal application stuff, like debug info, errors, etc. We'll use ding's ILoggerAware interface in our beans, giving them direct access to the logger of the container (this needs log4php, so may want to install it before proceeding).
  • Handle errors through Ding error handler helper
  • Handle signals through Ding signal handler helper
  • Handle shutdown through Ding shutdown handler helper

Application tree layout overview. What's where.

The key here, are the xml files, which are the container configuration. This is where all the beans are defined and related to each other. Because of this configuration, our beans will be injected with everything they need to work (even a logger :))

Main entry point

The main entry point for AsterTrace is astertrace.php. So you can start it by running it:

This script will call the bootstrap.php file that resides in the same directory. Boostrap.php will:

  • Setup the include path needed
  • Check the invoking arguments (remember, if this is a command line application, you need one argument which is the config directory where all configuration files reside, typically, ./conf)
  • If on a web environment, check the environment variable "CONFIG_DIR" to get the application's config directory.
  • Setup the ding's container configuration

After this, astertrace.php will continue execution:

  • The ding container will be instantiated using the configuration that bootstrap.php prepared.
  • The container will use the cli.xml file as its configuration (if cli environment), or the rest.xml (if on web environment).
  • Either beans file uses the PropertiesHolder to make ding load properties from php.properties, mysql.properties, astertrace.properties. So the correct values are injected in any beans that need them.
  • Either beans file includes other bean configurations, for PDO, event listeners, error handler, signal handler, and shutdown handler, so all of them are available in the application.
  • The bean pamiHelper is requested to the container. This bean is actually the PamiHelper that comes from ding. The container will also create the bean pami which is our own event handler.
  • An infinite loop is started, calling the process() method of the PamiHelper. This method will read messages incoming from AMI.

And that is pretty much all we have to do :) the container takes care of the rest:

  • Whenever an event is received by PAMI, it will call the PamiHelper.
  • The PamiHelper will call our own handler.
  • Our handler will dispatch an event through the container, named "anyEvent", so any beans listening for this event will execute.
  • Based on the name of the event, the handler dispatches another event through the container. For example, if the event was Dial, then the "dial" event is dispatched, and the method onDial() of every listening bean (for that event) is executed.
  • Make a telnet connection to the address:port configured in server.properties. You should see the events coming in from ami in json format :)
  • Setup your web server so you can access rest.php. Remember to setup the environment variable CONFIG_DIR to point to the application environment. When requesting rest.php, you should start seeing events in json format coming in from ami.

Currently available listeners

Event Listener

VarSet Listener

Dial Listener

DTMF Listener

Newchannel Listener

Newstate Listener

Newexten Listener

Extending your VoIP application to listen for more PBX Events

We can add more beans to extend this functionality. Adding beans is trivial, we could use annotations, yaml, or xml configurations.

Whenever we want to get a specific event, we just need to define a listens-on directive in the bean configuration, and the container will automagically call the method onEventName() with the event received :) So we can propagate events via Radius, or ActiveMQ, or RabbitMQ, etc.

Start creating your own VoIP applications (like Operator Panels) for Asterisk

As you see, having Ding and PAMI work together is a pretty smooth task, and will benefit your code by making it cleaner and less coupled. You will be able to focus on the task you really want to do. Ding will take care of the inversion of control and dependency injection, while PAMI will take care of the managing the AMI protocol. In this way, your application is truly event driven, and everything will get called without you having to worry about how or when. Even configuring the application is trivial due to the use of the PropertiesHolder.

Everything is a bean. The PDO object, the PDO statements, and error/shutdown/signal handlers, our own event handlers, etc. This allows the application to be easily extended (just write new beans that "listens-on" different events, or event the same). Everything can be done within extremely few lines of code :)