Project Stanley is now PHPARI – https://github.com/greenfieldtech-nirs/phpari

The information below is here for reference only, please follow the github link above.


Step 1: Bootstrapping

In order to get anything done, we will need to have our code initially load our composer autoload section, following that, we would like to bind our WebSocket client to Asterisk’s ARI socket. This can be achieved using our controller Constructor.

    function __construct()
    {

        parent::__construct();

        try {
            /*
             * Bootstrap exception handling object
             */
            $this->load->helper('stanley_exception_helper');

            /*
             * Bootstrap Logger and Worker Factory
             */
            $this->stasisLoop = \React\EventLoop\Factory::create();

            $this->stasisLogger = new \Zend\Log\Logger();
            $this->logWriter = new Zend\Log\Writer\Stream("php://output");
            $this->stasisLogger->addWriter($this->logWriter);

            /*
             * Setup your ARI login information here - normally, this will be in an external configuration
             */
            $this->ari_host = "my_ari_server_ip_number:8088";
            $this->ari_url = "http://" . $this->ari_host . "/ari";
            $this->stasis_application = "hello-world";
            $this->ari_username = "my_ari_user";
            $this->ari_password = "my_ari_password";

            /*
             * Now, we will setup our PEST instance
             */
            $this->ari_endpoint = new Pest($this->ari_url);
            $this->ari_endpoint->setupAuth($this->ari_username, $this->ari_password, "basic");

            $this->stasisClient = new \Devristo\Phpws\Client\WebSocket("ws://" . $this->ari_host . "/ari/events?api_key=" . $this->ari_username . ":" . $this->ari_password . "&app=" . $this->stasis_application, $this->stasisLoop, $this->stasisLogger);

        } catch (Exception $e) {
            die("Exception raised at file: " . __FILE__ . " Line: " . __LINE__);
        }

    }

Step 2: Connecting to ARI

Once we have our worker factory ready, it’s time to actually connect over to ARI and make sure we do something in there.

    public function index()
    {
        try {

            $this->load->model('stanley/M_ari_channels', 'channels');

            $this->stasisClient->on("request", function ($headers) {
                $this->stasisLogger->notice("Request object created!");
            });

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


            /* This is where the actual magic will happen */

            $this->stasisClient->open();
            $this->stasisLoop->run();

        } catch (Exception $e) {
            die("Exception raised at file: " . __FILE__ . " Line: " . __LINE__);
        }
    }

Step 3: Binding Events

Because ARI is asynchronous, we need our factory to respond to specific events that ARI will fire at it. The below section is incharge of that:

            $this->stasisClient->on("message", function ($message) {

                $messageData = json_decode($message->getData());
 
                /* We'll now add some proper functionality in here */
            });

Step 4: Doing something useful

Now, let’s tie all the loose ends together:

    public function index()
    {
        try {

            $this->load->model('stanley/M_ari_channels', 'channels');

            $this->stasisClient->on("request", function ($headers) {
                $this->stasisLogger->notice("Request object created!");
            });

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

            $this->stasisClient->on("message", function ($message) {

                $messageData = json_decode($message->getData());
                switch ($messageData->type) {
                    case "StasisStart":
                        $this->stasisLogger->notice("Stasis Start");
                        $lastResult = $this->channels->channel_playback($this->ari_endpoint, $messageData->channel->id, "sound:hello-world");
                        $this->stasisLogger->notice("Last result: " . $lastResult);
                        $lastResult = $this->channels->channel_playback($this->ari_endpoint, $messageData->channel->id, "sound:demo-congrats");
                        $this->stasisLogger->notice("Last result: " . $lastResult);
                        break;
                    case "StasisEnd":
                        $this->stasisLogger->notice("Stasis End");
                        break;
                    default:
                        $this->consoleWrite($message->getData($messageData));
                        break;
                }
            });

            $this->stasisClient->open();
            $this->stasisLoop->run();

        } catch (Exception $e) {
            die("Exception raised at file: " . __FILE__ . " Line: " . __LINE__);
        }
    }

Complete Code

load->helper('stanley_exception_helper');

            /*
             * Bootstrap Logger and Worker Factory
             */
            $this->stasisLoop = \React\EventLoop\Factory::create();

            $this->stasisLogger = new \Zend\Log\Logger();
            $this->logWriter = new Zend\Log\Writer\Stream("php://output");
            $this->stasisLogger->addWriter($this->logWriter);

            /*
             * Setup your ARI login information here - normally, this will be in an external configuration
             */
            $this->ari_host = "my_ari_server_ip_number:8088";
            $this->ari_url = "http://" . $this->ari_host . "/ari";
            $this->stasis_application = "hello-world";
            $this->ari_username = "my_ari_user";
            $this->ari_password = "my_ari_password";

            /*
             * Now, we will setup our PEST instance
             */
            $this->ari_endpoint = new Pest($this->ari_url);
            $this->ari_endpoint->setupAuth($this->ari_username, $this->ari_password, "basic");

            $this->stasisClient = new \Devristo\Phpws\Client\WebSocket("ws://" . $this->ari_host . "/ari/events?api_key=" . $this->ari_username . ":" . $this->ari_password . "&app=" . $this->stasis_application, $this->stasisLoop, $this->stasisLogger);

        } catch (Exception $e) {
            die("Exception raised at file: " . __FILE__ . " Line: " . __LINE__);
        }

    }

    public function index()
    {
        try {

            $this->load->model('stanley/M_ari_channels', 'channels');

            $this->stasisClient->on("request", function ($headers) {
                $this->stasisLogger->notice("Request object created!");
            });

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

            $this->stasisClient->on("message", function ($message) {

                $messageData = json_decode($message->getData());
                switch ($messageData->type) {
                    case "StasisStart":
                        $this->stasisLogger->notice("Stasis Start");
                        $lastResult = $this->channels->channel_playback($this->ari_endpoint, $messageData->channel->id, "sound:hello-world");
                        $this->stasisLogger->notice("Last result: " . $lastResult);
                        $lastResult = $this->channels->channel_playback($this->ari_endpoint, $messageData->channel->id, "sound:demo-congrats");
                        $this->stasisLogger->notice("Last result: " . $lastResult);
                        break;
                    case "StasisEnd":
                        $this->stasisLogger->notice("Stasis End");
                        break;
                    default:
                        $this->consoleWrite($message->getData($messageData));
                        break;
                }
            });

            $this->stasisClient->open();
            $this->stasisLoop->run();

        } catch (Exception $e) {
            die("Exception raised at file: " . __FILE__ . " Line: " . __LINE__);
        }
    }

    public function consoleWrite($jsonData)
    {
        try {
            $jsonObject = json_decode($jsonData);
            switch ($jsonObject->type) {
                case "PlaybackStarted":
                case "PlaybackFinished":
                    $this->stasisLogger->notice("Channel: " . $jsonObject->playback->target_uri . " Event: " . $jsonObject->type . " Media URI: " . $jsonObject->playback->media_uri . " State: " . $jsonObject->playback->state );
                    //$this->stasisLogger->notice($jsonObject);
                    break;
                default:
                    $this->stasisLogger->notice($jsonData);
                    break;
            }
            return 0;

        } catch (Exception $e) {
            die("Exception raised at file: " . __FILE__ . " Line: " . __LINE__);
        }
    }
}