Howto:SOAP API PHP5 Sample Code
Applies To
This information applies to
- V9 and later innovaphone PBX platform
- web server running PHP 5
innovaphone PBX web services can be used from PHP easily. Here is how
More Information
Configuration
You will need php with the SoapClient class available.
The innovaphone PBX wsdl file is required. See Reference8:SOAP API for information how to obtain this file.
To access older WSDL versions, you will need to change the WSDL URL setting (in const ___wsdl
) and adapt to the required number of version booleans in the call to Initialize()
in the pbx wrapper class.
If you have access to the internet, you can try out the sample code given below right away. Of course you may also adapt the sample script configuration and try with your own innovaphone PBX.
Running the Sample Code
To demonstrate SOAP usage with PHP, a simple script is presented that will allow you to initiate a call using a web page.
The following class innoPBX is used to wrap the (somewhat clumsy) PHP SOAP access mechanism:
<?php
// innovaphone PBX SOAP API PHP wrapper class
//
class innoPBX extends SOAPClient {
protected $___key; // the session key
protected $___session; // the session id
protected $___options = array(
// default SOAPClient::__construct options used by the class
"connection_timeout" => 10,
"exceptions" => true,
);
const ___wsdl = 'http://www.innovaphone.com/wsdl/pbx900.wsdl';
// class constructor
public function __construct(
$server, // the PBX IP
$httpu, // the HTTP user id (e.g. "admin")
$httpp, // the HTTP password (e.g. "ip800")
$user = null, // the PBX user CN to work with
$options = null,
// extra or overriding options for SOAPClient::__construct
$wsdl = null // the wsdl file location
) {
$wsdl = ($wsdl === null) ? self::___wsdl : $wsdl;
$usedoptions = array( // forced options
'login' => $httpu,
'password' => $httpp,
'location' => "http://$server/PBX0/user.soap",
);
if (is_array($options)) $usedoptions += $options;
// merge in user options
$usedoptions += $this->___options; // merged in class global options
// construct parent class
parent::__construct($wsdl, $usedoptions);
// get the connection (using and activating v9 wsdl)
$init = $this->Initialize($user, "PHP SOAP Wrapper", true, true, true, true, true);
$this->___key = $init['key'];
$this->___session = $init['return'];
}
public function key() { return $this->___key; }
public function session() { return $this->___session; }
}
?>
Save this code into a file named innopbx.class.php on your web server. This class is of course not required to use PHP SOAP with the innovaphone PBX API. However, it comes handy and should be generic enough to be useful in most cases.
The SOAP client uses the following code:
<?php
// get the innoPBX wrapper class
require_once('innopbx.class.php');
// dummy classes to map SOAP results to (really would love to use namespaces here...)
// you can add methods and variables to these classes as needed
class innoUserInfo { };
class innoCallInfo { };
class innoAnyInfo { };
class innoGroup { };
class innoNo { };
class innoInfo { };
// config, adapt to your need
// this data below will work with innovaphones demo PBX. Just register 2 phones as user-3 and user-4 and hve fun!
$server = "demo.innovaphone.com";
$user = "SOAP";
$httpu = "demo";
$httpp = "demo";
$caller = "PBX User Four";
$destination = "130";
print "
"; // create connector to PBX // class mapping is optional $inno = new innoPBX($server, $httpu, $httpp, $user, array('classmap' => array("UserInfo" => "innoUserInfo", "CallInfo" => "innoCallInfo", "AnyInfo" => "innoAnyInfo", "Group" => "innoGroup", "No" => "innoNo", "Info" => "innoInfo", ))); if ($inno->key() == 0) die("failed to login to PBX"); // show all methods supported by this object print "All the functions provided by class innoPBX:\n"; print_r($inno->__getFunctions()); print "\n\n"; // get version info $v = $inno->Version(); // show both types of infos list function showInfos(&$poll, $head, $cn = "", $user = "", $call = "") { print $head . "\n"; if ($cn !== null) { print count($poll->user) . " UserInfos\n"; foreach($poll->user as $ui) { if (($cn === "") || ($cn == $ui->cn)) { print " {$ui->cn} ({$ui->h323} #{$ui->e164}) state {$ui->state}\n"; } } } if ($call !== null) { print count($poll->call) . " CallInfos\n"; foreach($poll->call as $ci) { if ((($user === "") || ($user == $ci->user)) && (($call === "") || ($call == $ci->call))) { print " {$ci->user}/{$ci->call} {$ci->No[1]->h323} #{$ci->No[1]->e164} (remote {$ci->No[0]->h323} #{$ci->No[0]->e164}) msg {$ci->msg}\n"; } } } } // retrieve the full user list. Foreach object in the PBX, one userinfo is posted, terminated by an empty one // You cannot assume that you will receive this list within a certain number of Poll results, so please iterate print "Retrieving User list for "; foreach ($v as $name => $value) print "\n $name=$value "; print "...\n\n"; $seen = false; $i = 1; while (!$seen) { $p = $inno->Poll($inno->session()); showInfos($p, "Poll() result #$i", "", null, null); $i++; if ($p->user[count($p->user)-1]->cn == "") { // we have seen all entries print " --- END OF LIST ---\n\n"; $seen = true; break; } } // create a call on behalf of some user // first we need a user handle print "\nObtaining a user handle for $caller..."; $uhandle = $inno->UserInitialize($inno->session(), $caller); print " $uhandle\n"; if ($uhandle == 0) { die("cant get user handle for $caller"); } // then we create a call print "Creating call from $caller to $destination..."; $call = $inno->UserCall($uhandle, null, $destination, null, 0, array()); print " $call\n"; if ($call == 0) { die("cant call on behalf of user $caller ($uhandle)"); } // get call state(s) until call disappears // note: you must at least poll for one call info related to this call, if you end // the session right after UserCall, the call will not succeed! $done = false; while (!$done) { ob_flush(); // this is just to see the progress on the call states $p = $inno->Poll($inno->session()); showInfos($p, "Call States #$i for #{$call}", $caller, $uhandle, $call); $i++; // see if there was a del event (there is ALWAYS a del event) // we could as well look for an event with active = false foreach ($p->call as $ci) { if (($ci->user == $uhandle) && ($ci->call == $call) && ($ci->msg == "del")) { print "\nCall terminated!\n"; $done = true; } } } // terminate the session $e = $inno->End($inno->session()); ?>
Save this into a file named innopbx.sample.php in the same directory of your web server. To run the code, you need to examine and possibly adapt the configuration parameters in innopbx.sample.php:// config, adapt to your need // this data below will work with innovaphones demo PBX. Just register 2 phones as user-3 and user-4 and hve fun! $server = "demo.innovaphone.com"; $user = "SOAP"; $httpu = "demo"; $httpp = "demo"; $caller = "PBX User Four"; $destination = "130";
This configuration will work right away with our publicly available demo PBX. You simply need to register 2 phones with the user-3 and user-4 users. Of course you can also use your own PBX. Set the configuration parameters as follow:
$server | IP address of your innovaphone PBX |
$user | CN (aka Long name) of a PBX user that is active group member of a group where all others are (active or passive) members of |
$httpu | the admin account of the box running the innovaphone PBX (e.g.admin) |
$httpp | the admin password of this box (e.g. ipxxx) |
$caller | CN (aka Long name) of a PBX user you will have the script make a call for |
$destination | the number to be called |
How the code works
The innovaphone PBX API wrapper class
This class is derived from (extends in PHP speak) the standard SOAPClient class. It basically simplifies the use of the standard class and performs the required call to Initialize within its constructor. The constructor takes the following arguments:
public function __construct($server, $httpu, $httpp, $user, $options = null, $wsdl = null);
$server, $httpu, $httpp, $user | see table above |
$options | an optional array of further options to SOAPClient::__construct. Normally not required. See the PHP SOAPClient class documentation for details. |
$wsdl | the optional URI of the pbx wsdl file. Please note that you should use pbx10_00.wsdl instead of pbx.wsdl, as this is the more recent version of the wsdl. By default, the wsdl is received from the innovaphone web and (subject to PHP configuration options) cached in PHP's wsdl cache. If you intend to supply it locally (which is recommended, as you otherwise depend on www.innovaphone.com being available each time your program runs), use a file:// URI. |
$v = $inno->Version();
To see the functions available and their PHP signatures, you may call
$inno = new innoPBX(...);
print_r($inno->$inno->__getFunctions());
Many PBX SOAP API functions required either the session-key or session-handle. These are available from the class instance for convenience:
$p = $inno->Poll($inno->session());
How to use the innovaphone PBX API wrapper class with SSL
In order to make a secure connection to the PBX, you have to change the inno.class.php as follows:'location' => "https://$server/PBX0/user.soap"
Note: in PHP 5.6 all encrypted client streams now enable peer verification by default. By default, this will use OpenSSL's default CA bundle to verify the peer certificate.
See: OpenSSL changes in PHP 5.6.x
To disable peer verification for the soap connection, you have to pass this as an option to the constructor:
$context = stream_context_create(array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => false
)
));
$options = array('stream_context' => $context);
$inno = new innoPBX($server, $httpu, $httpp, $user, $options, $wsdl);
More information on stream_context_create and ssl options:
stream_context_create and
SSL context options
How PHP maps SOAP parameters to method parameters and results
The mapping of parameters is pretty straight forward. Arguments passed in to a SOAP method are passed as a value parameters to the class methods. The thing to understand is that return values are always passed as return values, never as by-reference method arguments. If a SOAP method returns a single item, then it is returned by the function directly with appropriate type:
int UserInitialize(int $session, string $user);
The single return value from the UserInitialize SOAP method is returned as int by the UserInitialize innoPBX-method.
If the SOAP methods returns multiple items, then the corresponding class method will return an array:
array Initialize(string $user, string $appl);
will return an array such as
array(
['return'] => value
['key'] => value
)
In the method signature printed out by __getFunctions() this looks like
list(int $return, int $key) Initialize(string $user, string $appl)
The sample class user
This section will discuss the sample code. It is not a decent description of the PBX SOAP API. For this, see the related articles. A new innoPBX class instance object $inno is created using the values from the configuration variables discussed above.
$inno = new innoPBX($server, $httpu, $httpp, $user,
array('classmap' => array("UserInfo" => "innoUserInfo",
"CallInfo" => "innoCallInfo",
"AnyInfo" => "innoAnyInfo",
"Group" => "innoGroup",
"No" => "innoNo",
"Info" => "innoInfo",
)));
The option fifth argument will add a classmap entry to the options passed to SOAPClient::__construct. If this is done, the SOAP class will use your defined classes when returning values to the caller. Otherwise, structured return values will be indexed arrays (using the element names as array indexes). This is neat, but not required for the class to function.
When Initialize (which is done implicitly in the innoPBX constructor) was successfull, one UserInfo class object per PBX object is posted to the caller. This list is terminated by an empty entry (more precisely, an entry with an empty cn member).
The
while (!$seen) {
$p = $inno->Poll($inno->session());
...
loop will retrieve and show all these entries.
To be able to perform calls, a user session must be opened by
$uhandle = $inno->UserInitialize($inno->session(), $caller);
The $uhandle retrieved will identify the user to be worked with on subsequent calls.
A call on behalf of $caller is then initiated:
$call = $inno->UserCall($uhandle, null, $destination, null, 0, array());
When UserCall returns a non-zero call handle, the call is initiated and can be monitored by examining CallInfo events returned by subsequent Poll() calls:
while (!$done) {
$p = $inno->Poll($inno->session());
this will retrieve events from the PBX
if (($ci->user == $uhandle) &&
($ci->call == $call) &&
($ci->msg == "del")) {
print "\nCall terminated!\n";
$done = true;
until a del event is seen for the call previously created by UserCall. Please note that any number of call events may occur, e.g. because someone else called $caller. You cannot assume that all events retrieved are related to the call you created. Also, any number of UserInfo events may be retrieved.
Please note that there is no need to consume all of the events related to the call for it to work. However, you must not terminate the session before the call is actually proceeding. If you do so, strange things will happen. To be sure the call proceeds, it is ok to wait for just any CallInfo event related to the call. You can then safely terminate your session and exit the web page.
Working with Call States from CallInfo
The state field of the CallInfo contains a bit field encoded information about direction of the call, hold information and an actual call state. Here is an example how to get relevant states:
<?php
// decimal state value from CallInfo
// $callstate = 2;
// call state?
switch ($callstate & 0xF) {
case 1:
echo ("setup\r\n");
break;
case 2:
echo ("setup-ack\r\n");
break;
case 3:
echo ("call-proc\r\n");
break;
case 4:
echo ("Alert\r\n");
break;
case 5:
echo ("Connect\r\n");
break;
case 6:
echo ("disconnect sent\r\n");
break;
case 7:
echo ("disconnect received\r\n");
break;
case 8:
echo ("parked\r\n");
break;
default:
echo ("UNKNOWN STATE: " . ($callstate & 0xF) . "\r\n");
break;
}
// call direction?
switch ($callstate & 0x80) {
case 0:
echo ("inbound call\r\n");
break;
case 128:
echo ("outbound call\r\n");
break;
default:
echo ("UNKNOWN STATE: " . ($callstate & 0x80) . "\r\n");
break;
}
// is call on hold?
switch ($callstate & 0x100) {
case 0:
echo ("active call\r\n");
break;
case 256:
echo ("call on hold\r\n");
break;
default:
echo ("UNKNOWN STATE: " . ($callstate & 0x100) . "\r\n");
break;
}
// is call being held by peer?
switch ($callstate & 0x200) {
case 0:
echo ("active call\r\n");
break;
case 512:
echo ("active call put on hold by peer\r\n");
break;
default:
echo ("UNKNOWN STATE: " . ($callstate & 0x200) . "\r\n");
break;
}
?>
More Sample Code
Much more elaborate SOAP sample code is available for download from samples section on http://download.innovaphone.com. Note that if you want to use innovaphone's demo PBX to test the code, you need to change the PBX IP address from 145.253.157.200 todemo.innovaphone.com
in the downloaded code.
This code shows how to deal with call control on master/slave scenarios as well as how to perform PBX configuration updates or PBX object creation using the SOAP Admin function. Currently, there are the following sample functions available:
* create a SOAP session to the master PBX
* create a user session on a device
* initiate a call via SOAP and terminate it once connected
* List all available SOAP functions
* Show an object's configuration using the Admin() function
* clone of adminshow which forces config=1
* Modify a user object using the Admin() function
* Clone a PBX object using the Admin() function
* Add call forwarding using Admin() function
* Delete call forwarding using Admin() function
* Get the PBX key
* Generate an encrypted PBX user password
* Set a new password for user
* Get the password of a PBX object in clear
* Decrypt a password from VARs (only works for boxes with default admin password!)
* Encrypt a password for use in VARs (only works for boxes with default admin password!)
* Find matching users
Although this code is written in PHP5, it is also good reading if you do not intend to use PHP as it exemplifies the principles of using SOAP and the SOAP Admin() function.