An introduction to PHPARI

PHPARI is a class library and development framework, designed to make the development of Asterisk ARI applications a breeze. Technically speaking, PHP isn’t the natural choice for developing ARI applications and the reason for that is simple: ARI is Asynchronous, most PHP developers aren’t familiar with Asynchronous programming. In order to overcome this issue, the PHP React library is used, in order to provide a means of developing Asynchronous applications with PHP.

The following tutorial will teach you how to get ARI and PHPARI off the ground and up and running.

Step 1: Get ARI Working on Asterisk

As ARI is a WebSocket technology, so naturally, we’ll need to enable the Asterisk HTTP server. So, let’s do that first, open the /etc/asterisk/http.conf file for editing:

[general]
enabled=yes
bindaddr=your.server.ip.address
bindport=8088
;prefix=asterisk
Minimal configuration

** Pay attention to the prefix setting above, it is currently disabled. When enabled, a prefix will be assigned to the Asterisk http server URL, so accessing WebSockets and other Asterisk http resources, will be via the prefix routing.

If you would like to support TLS (encrypted web sockets), add the following:

tlsenable=yes          ; enable tls - default no.
tlsbindaddr=your.server.ip.number:8089
tlscertfile=/etc/asterisk/generated.key.file

Following the above configuration, the web server should be fully configured. Now, we need to configure the ARI connector, done via editing the /etc/asterisk/ari.conf file:

[general]
enabled = yes       
pretty = yes     

[ariuser]
type = user
read_only = no
password = 6a1fd5dbc84a3d64fe31b8854019ec56971eaec8b3ada0da83bf4bd5db30245f
Minimal ari.conf configuration

Step 2: PHPARI Installation

The preferred method of installation is via composer. You may find additional information about the actual composer.json file for this at http://www.packagist.org.

The recommended file is as follows:

{
    "require": {
        "php": ">=5.3.9",
        "educoder/pest": "1.0.0",
        "devristo/phpws": "dev-master",
        "greenfieldtech-nirs/phpari": "dev-master"
    }
}
composer.json file to use phpari

We recommend working with the dev-master package, as the code is being maintained on a daily basis.

Step 3: The basic Stasis Application Template

Before you can continue working with PHPARI, you need to setup the phpari.ini file. Here is a sample of what your file should look like:

[general]
debug=0
logfile=console ; console for direct console output, filename for file based logging

[asterisk_ari]
username=testuser
password=testing
host=your.server.ip.address
port=8088
endpoint=/ari
transport=ws ; ws for none encrypted, wss for encrypted (currently, only ws is supported)

[asterisk_manager]
username=amiuser
password=amipassword
host=your.server.ip.address
port=5038
Sample phpari.ini file configuration

** Pay attention, if your /etc/asterisk/http.conf file indicated a prefix, your endpoint value should include the prefix. For example, if you’ve set the prefix to ‘asterisk’, your endpoint URL shall be /asterisk/ari.

Remember: You are required to change the IP addresses in the samples, or it will not work !!!

<?php 

/*
 * 
 * phpari - A PHP Class Library for interfacing with Asterisk(R) ARI 
 * Copyright (C) 2014 Nir Simionovich 
 * 
 * This library is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation; either 
 * version 2.1 of the License, or (at your option) any later version. 
 * 
 * This library is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * Lesser General Public License for more details. 
 * 
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this library; if not, write to the Free Software 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 
 * Also add information on how to contact you by electronic and paper mail. 
 * 
 * Greenfield Technologies Ltd., hereby disclaims all copyright interest in 
 * the library `phpari' (a library for creating smart telephony applications) 
 * written by Nir Simionovich and its respective list of contributors. 
 */ 

require_once "../vendor/autoload.php"; 

class BasicStasisApplication { 

    private $ariEndpoint; 
    private $stasisClient; 
    private $stasisLoop; 
    private $phpariObject; 
    private $stasisChannelID; 
    private $dtmfSequence = ""; 
    public $stasisLogger; 

    public function __construct($appname = NULL) 
    { 
        try { 

            if (is_null($appname)) 
                throw new Exception("[" . __FILE__ . ":" . __LINE__ . "] Stasis application name must be defined!", 500); 

                $this->phpariObject = new phpari($appname);
                $this->ariEndpoint  = $this->phpariObject->ariEndpoint;
                $this->stasisClient = $this->phpariObject->stasisClient;
                $this->stasisLoop   = $this->phpariObject->stasisLoop;
                $this->stasisLogger = $this->phpariObject->stasisLogger;
                $this->stasisEvents = $this->phpariObject->stasisEvents;

            } catch (Exception $e) {
                echo $e->getMessage();
                exit(99);
            }
        }

        public function setDtmf($digit = NULL)
        {
            try {

                $this->dtmfSequence .= $digit;

                return TRUE;

            } catch (Exception $e) {
                return FALSE;
            }
        }

        // process stasis events
        public function StasisAppEventHandler()
        {
            $this->stasisEvents->on('StasisStart', function ($event) {
                $this->stasisLogger->notice("Event received: StasisStart");
                $this->stasisChannelID = $event->channel->id;
                $this->phpariObject->channels()->channel_answer($this->stasisChannelID);
                $this->phpariObject->channels()->channel_playback($this->stasisChannelID, 'sound:demo-thanks', NULL, NULL, NULL, 'play1');
            });

            $this->stasisEvents->on('StasisEnd', function ($event) {
                $this->stasisLogger->notice("Event received: StasisEnd");
                $this->phpariObject->channels()->channel_delete($this->stasisChannelID);
            });


            $this->stasisEvents->on('PlaybackStarted', function ($event) {
                $this->stasisLogger->notice("+++ PlaybackStarted +++ " . json_encode($event->playback) . "\n");
            });

            $this->stasisEvents->on('PlaybackFinished', function ($event) {
                switch ($event->playback->id) {
                    case "play1":
                        $this->phpariObject->channels()->channel_playback($this->stasisChannelID, 'sound:demo-congrats', NULL, NULL, NULL, 'play2');
                        break;
                    case "play2":
                        $this->phpariObject->channels()->channel_playback($this->stasisChannelID, 'sound:demo-echotest', NULL, NULL, NULL, 'end');
                        break;
                    case "end":
                        $this->phpariObject->channels()->channel_continue($this->stasisChannelID);
                        break;
                }
            });

            $this->stasisEvents->on('ChannelDtmfReceived', function ($event) {
                $this->setDtmf($event->digit);
                $this->stasisLogger->notice("+++ DTMF Received +++ [" . $event->digit . "] [" . $this->dtmfSequence . "]\n");
                switch ($event->digit) {
                    case "*":
                        $this->dtmfSequence = "";
                        $this->stasisLogger->notice("+++ Resetting DTMF buffer\n");
                        break;
                    case "#":
                        $this->stasisLogger->notice("+++ Playback ID: " . $this->phpariObject->playbacks()->get_playback());
                        $this->phpariObject->channels()->channel_continue($this->stasisChannelID, "demo", "s", 1);
                        break;
                    default:
                        break;
                }
            });
        }

        public function StasisAppConnectionHandlers()
        {
            try {
                $this->stasisClient->on("request", function ($headers) {
                    $this->stasisLogger->notice("Request received!");
                });

                $this->stasisClient->on("handshake", function () {
                    $this->stasisLogger->notice("Handshake received!");
                });

                $this->stasisClient->on("message", function ($message) {
                    $event = json_decode($message->getData());
                    $this->stasisLogger->notice('Received event: ' . $event->type);
                    $this->stasisEvents->emit($event->type, array($event));
                });

            } catch (Exception $e) {
                echo $e->getMessage();
                exit(99);
            }
        }

        public function execute()
        {
            try {
                $this->stasisClient->open();
                $this->stasisLoop->run();
            } catch (Exception $e) {
                echo $e->getMessage();
                exit(99);
            }
        }

    }

    $basicAriClient = new BasicStasisApplication("hello-world");

    $basicAriClient->stasisLogger->info("Starting Stasis Program... Waiting for handshake...");
    $basicAriClient->StasisAppEventHandler();

    $basicAriClient->stasisLogger->info("Initializing Handlers... Waiting for handshake...");
    $basicAriClient->StasisAppConnectionHandlers();

    $basicAriClient->stasisLogger->info("Connecting... Waiting for handshake...");
    $basicAriClient->execute();

    exit(0);
Basic Stasis Application Template