Here is the PHP code that parses the number you click on using the Dial Plan you are testing. If you have any suggestions or enhancments then send your code to voip27@supremeit.com:
<?php
/**
* Dial plan parser
*
* @package DIALPLAN
* @author Andrew Braund <voip@supremeit.com>
* @copyright 2005 Andrew Braund
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version 0.1 alpha
* @link http://supremeit.com/voip/doDialPlan.php
* @todo sipura/PAP2 differences etc
* @todo number barring
* @todo S:4 (...) to set timeouts in PAP2
* @todo dial tone (,)
*/
class DIALPLAN {
/**
* Set the debug flag so we echo some extra info useful during development
* If set to false, nothing will be echoed
* @var false|true
*/
var $debug = false;
/**
* Set the showPreg flag show the preg string we use in the returned allSearch
* If set to false, allSearch strings will only contain the search
* @var false|true
*/
var $showPreg = true;
/**
* A couple of parameters, maximum length that we allow the phone number to be dialed
* and maximum length of a dial plan. 2047 limit is for Linksys PAP2
*
* @var int
*/
var $numberMaxLength = 12;
var $dialplanMaxLength = 2047;
/**
* Set error string
* If set to false, no error else error string
* @var false|string
* @access public
*/
var $errorStr = true;
/**
* An array holding all dial plans in their raw format
*
* key is just an numeric index
*
* row format is array('000S0','xxxxxx','<:61>03xxxxxxx')
*
* @var object
* @access private
*/
var $_allRaw;
/**
* An array holding all dial plans in their search format
*
* key is just an numeric index
*
* row format is array('000S0','xxxxxx','<:61>03xxxxxxx')
*
* @var object
* @access private
*/
var $_allSearch;
/**
* An array holding the index to each dial plan that was "hit" during parsing
*
*
* row format is array('3','4','6')
*
* @var array
* @access private
*/
var $_hit= array();
/**
* An integer holding the index to each dial plan that is to be used
*
* @var int
* @access private
*/
var $_used=-1;
/**
* An string holding the new number
*
* @var int
* @access private
*/
var $_newNumber=-1;
/**
* Get the new phone number to dial
*
* @param none
* @return string the number
* @access public
*/
function getNewNumber(){
return $this->_newNumber;
}
/**
* Get a list of all hits
*
* @param none
* @return array the list
* @access public
*/
function getHits(){
return $this->_hit;
}
/**
* Get the dial plan that is to be used
*
* @param none
* @return int
* @access public
*/
function getUsed(){
return $this->_used;
}
/**
* Get a list of all dial plan items in raw format
*
* list format is array('000S0','xxxxxx','<:61>03xxxxxxx')
*
* @param none
* @return array the list
* @access public
*/
function getAllRaw(){
return $this->_allRaw;
}
/**
* Get a list of all dial plan items in search format
* this allow you to see what the parser is looking for
* more easily as all the substitutions and special tags have been removed
*
* list format is array('000','xxxxxx','03xxxxxxx');
*
* @param none
* @return array the list
* @access public
*/
function getAllSearch(){
return $this->_allSearch;
}
function doDialPlan($dialPlan='(1|2|3|4|5)',$number='1800111222') {
$rtnAry['gw'] = '';
$this->_used=-1;
$this->errorStr=false;
// get rid of any whitespace
//$dialPlan = preg_replace('/ |\n|\r|(\r\n)/m', '', $dialPlan);
$dialPlan = preg_replace('/\s/', '', $dialPlan);
// break out dial plan into individual items
//$dialPlan = trim($dialPlan);
$uncleanDialPlan = $dialPlan;
$dialPlan = preg_replace('/[^0-9Sgwx*!,.#:<>|@\-\(\)\[\]]/', '', $dialPlan);
$uncleanNumber = $number;
if (strlen($number) > $this->numberMaxLength) {
$this->errorStr = 'Phone number length was greater than '.$this->numberMaxLength.' characters';
} else if ($uncleanDialPlan != $dialPlan) {
$this->errorStr = 'Dial plan may only contain the characters 0-9xgwS-*<>@#:.';
} else if (strlen($dialPlan) > $this->dialplanMaxLength ) {
$this->errorStr = 'Dial plan length was greater than '.$this->dialplanMaxLength.' characters';
} else if (preg_replace('/[^0-9*#]/', '', $number) != $uncleanNumber) {
$this->errorStr = 'Number may only contain the characters 0-9*#';
} else if (!(substr($dialPlan,0,1) == '(' && substr($dialPlan,-1,1) == ')')) {
$this->errorStr = 'Dial plan must be enclosed in ()<br />';
$this->errorStr .= htmlentities(substr($dialPlan,0,10));
$this->errorStr .= ' ... '.htmlentities(substr($dialPlan,-10,11));
} else { // not a basic error
$dialPlan = substr($dialPlan,1,strlen($dialPlan)-2); // nuke ()
$dialPlanAry = explode('|',$dialPlan);
if ($this->debug ) {
echo 'dialPlan = '.$dialPlan.' number = '.$number."<br />\n";
echo '<table border="0" cellspacing="2" cellpadding="0"><tr>'."\n";
echo '<th>key</th><th>val</th>';
echo '<th>searchStr</th><th>preg</th><th>number</th>';
echo "<th>hit</th><th>replaceStr</th><th>removeStr</th><th>New #</th></tr>\n";
}
$rtnAry['gw'] = 'GW1'; // default gateway unless set below
$gw='not set';
$i = 0;
foreach ($dialPlanAry as $key=>$val) {
if ($this->debug) echo '<tr><td>'.$key.'</td><td>'.$val."</td>\n";
$searchStr = $val;
$searchStr = str_replace('*','\*',$searchStr);
$searchStr = preg_replace('/<([^:]*):[^>]*>/', '${1}', $searchStr);
// nuke any immediate dial flags (S0)
$searchStr = preg_replace('/S0/', '', $searchStr);
// convert repeat digit x. to a regex
$pregStr = preg_replace('/(.)\./','[${1}]*',$searchStr);
// convert dialplan to a regex
$pregStr = preg_replace('/x/','.',$pregStr);
if ($this->debug) echo '<td> '.$searchStr.'</td><td>'.$pregStr.'</td><td>'.$number.'</td>';
// replacement
$count = 0;
$replaceStr = preg_replace('/^<([^:]*):([^>]*)>(.*)/', '${2}', $val);
$removeStr = preg_replace('/^<([^:]*):([^>]*)>(.*)/', '${1}', $val);
if($removeStr == $val) { // didn't actually replace anything
$removeStr = '';
}
if($replaceStr == $val) { // didn't actually replace anything
$newNumber = $number;
$replaceStr = '';
} else {
$newNumber = preg_replace('/^'.$removeStr.'(.*)/',$replaceStr.'${1}',$number);
}
if (preg_match('/^'.$pregStr.'/',$number)) {
if ($this->debug) echo '<td>'.$i."</td>\n";
$this->_hit[] = $i; // we record a list of all the search strings we hit
if ($this->_used == -1) { // for first one hit
$this->_used = $i;
$this->_newNumber = $newNumber; // update class var only for used string
// get the gateway from the search string we have just hit
$gw = preg_replace('/.*:@(gw.)>/', '${1}', $val);
if ($gw != $val) {
$rtnAry['gw'] = $gw;
}
}
if ($this->debug) echo '<td>'.$replaceStr.'</td><td>'.$removeStr."\n";
if ($this->debug) echo '<td>'.$newNumber."</td>\n";
} else {
if ($this->debug) echo '<td colspan="3"> </td>'."\n ";
}
if ($this->showPreg) {
$this->_allSearch[] = $searchStr.' '.$pregStr;// .' gw='.$gw;
} else {
$this->_allSearch[] = $searchStr;
}
$i++;
if ($this->debug) echo "</tr>\n ";
}
$this->errorStr = false;
//$rtnAry['allRaw'] = $dialPlanAry;
$this->_allRaw = $dialPlanAry;
}
if ($this->debug) echo "</table><br />\n ";
return $rtnAry;
}
}
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
?>
some simple unit tests (please suggest some more - code would be great!);
<?php
include 'doDialPlan.php';
require_once 'PHPUnit.php';
class DialPlanTest extends PHPUnit_TestCase {
var $dp;
function DialPlanTest($name) {
$this->PHPUnit_TestCase($name);
}
function setUp() {
$this->dp = new DIALPLAN();
}
function testGetAllRawBasic() {
$this->dp->doDialPlan('(<:61>xxxx| xxxxx| <2:613>2xxxx | 23233 |33)','23432342');
$dpAry = $this->dp->getAllRaw();
if(!is_array($dpAry)) {
$errormsg = 'dpAry was not an array! count';
} else {
$errormsg = 'entries in raw array';
}
$this->assertEquals(5, count($dpAry),$errormsg);
}
function testGetAllRaw() {
$this->dp->doDialPlan('(<:61>xxxx| xxxxx| <2:613>2xxxx | 23233 |33)','23432342');
$errormsg = 'values in raw array';
$expected = array('<:61>xxxx', 'xxxxx','<2:613>2xxxx','23233','33');
$this->assertEquals($expected, $this->dp->getAllRaw(), $errormsg);
}
function testGetAllSearch() {
$this->dp->showPreg = false; // don't add the preg version for this test
$this->dp->doDialPlan('(<:61>xxxx| xxxxx| <2:613>2xxxx | 23233 |33)','23432342');
$errormsg = 'values in search array';
$expected = array('xxxx','xxxxx','22xxxx','23233','33');
$this->assertEquals($expected, $this->dp->getAllSearch(), $errormsg);
}
function testHits() {
// $this->dp->debug = true;
$this->dp->showPreg = false; // don't add the preg version for this test
$this->dp->doDialPlan('(<:61>xxxx| xxxxx| <2:613>2xxxx |xx.| 239xx |33)','23999');
$dpAry = $this->dp->getHits();
if(!is_array($dpAry)) {
$errormsg = 'dpAry was not an array! count';
} else {
$errormsg = 'values in array';
}
$expected = array(0,1,3,4);
if($expected != $dpAry) {
foreach($dpAry as $key=>$val){
echo $key.' '.$val."<br />\n";
}
}
$this->assertEquals($expected, $dpAry, $errormsg);
}
function testUsed() {
// $this->dp->debug = true;
$this->dp->showPreg = false; // don't add the preg version for this test
$this->dp->doDialPlan('(<:61>xxxx| xxxxx| <2:613>2xxxx | 239xx |33)','23999');
// echo ' used = '.$this->dp->getUsed().'<br />';
$this->assertEquals(0, $this->dp->getUsed());
}
function testErrorStr() {
// $this->dp->debug = true;
$this->dp->showPreg = false; // don't add the preg version for this test
$this->dp->doDialPlan('(<:61>xxxx| xxxxx| <2:613>2xxxx | 239xx |33)','23u9999');
$errorStr = $this->dp->errorStr;
// echo 'errorStr is '.$errorStr."<br />\n";
if($errorStr === false) {
$errorStr = 'non';
$errormsg = 'thought number was good!';
} else if($errorStr === true) {
$errorStr = 'non';
$errormsg = 'default value of true!!';
} else {
$errormsg = 'a string at least';
}
// echo ' used = '.$this->dp->getUsed().'<br />';
$this->assertRegExp('/.*contain.*/', $errorStr, $errormsg);
}
function testReplace() {
$this->dp->debug = true;
$dialPlan = '( *xx | 0 | <0011:> xxxx x. | <:618> [8]xxx xxxx ';
$dialPlan .= '| <0:61> [2345679] xxxx xxxx | 61 x xxxx xxxx';
$dialPlan .= '| <1800:611800> 1800xxx xxx | <130:61130> x xxx xxx ';
$dialPlan .= '| <13:6113> xx xx | 1900 xxx xxx! )';
$this->dp->doDialPlan($dialPlan,'8229876');
$this->dp->doDialPlan('(<:61>xxxx| xxxxx|<2:613>2xxxx |33)','2222');
$this->dp->doDialPlan('(<:61>xxxx| xxxxx|<2:613>2xxxx |33)','2222');
$this->dp->doDialPlan('(<:61>xxxx| xxxxx|<2:613>2xxxx |33)','2229876');
$this->dp->doDialPlan('(<8:61>xxxx| xxxxx|<2:613>2xxxx |33)','8229876');
// echo ' used = '.$this->dp->getUsed().'<br />';
$this->assertEquals(0, $this->dp->getUsed());
}
}
$suite = new PHPUnit_TestSuite('DialPlanTest');
$result = PHPUnit::run($suite);
print $result->toHTML();
?>